<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <meta name="renderer" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=edge" >
  <link rel="dns-prefetch" href="http://example.com">
  <title>lcf Blog</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta property="og:type" content="website">
<meta property="og:title" content="lcf Blog">
<meta property="og:url" content="http://example.com/page/3/index.html">
<meta property="og:site_name" content="lcf Blog">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="乘风的小站">
<meta name="twitter:card" content="summary">
  
    <link rel="alternative" href="/atom.xml" title="lcf Blog" type="application/atom+xml">
  
  
    <link rel="icon" href="/img/favicon.ico">
  
  <link rel="stylesheet" type="text/css" href="/./main.0cf68a.css">
  <style type="text/css">
  
    #container.show {
      background: linear-gradient(200deg,#a0cfe4,#e8c37e);
    }
  </style>
  

  

<meta name="generator" content="Hexo 5.4.2"></head>

<body>
  <div id="container" q-class="show:isCtnShow">
    <canvas id="anm-canvas" class="anm-canvas"></canvas>
    <div class="left-col" q-class="show:isShow">
      
<div class="overlay" style="background: #4d4d4d"></div>
<div class="intrude-less">
	<header id="header" class="inner">
		<a href="/" class="profilepic">
			<img src="/img/yinshi.png" class="js-avatar">
		</a>
		<hgroup>
		  <h1 class="header-author"><a href="/"></a></h1>
		</hgroup>
		

		<nav class="header-menu">
			<ul>
			
				<li><a href="/">主页</a></li>
	        
				<li><a href="/archives">归档</a></li>
	        
			</ul>
		</nav>
		<nav class="header-smart-menu">
    		
    			
    			<a q-on="click: openSlider(e, 'innerArchive')" href="javascript:void(0)">所有文章</a>
    			
            
    			
    			<a q-on="click: openSlider(e, 'friends')" href="javascript:void(0)">友链</a>
    			
            
    			
    			<a q-on="click: openSlider(e, 'aboutme')" href="javascript:void(0)">关于我</a>
    			
            
		</nav>
		<nav class="header-nav">
			<div class="social">
				
					<a class="github" target="_blank" href="#" title="github"><i class="icon-github"></i></a>
		        
					<a class="csdn" target="_blank" href="https://blog.csdn.net/qq_35131503" title="csdn"><i class="icon-csdn"></i></a>
		        
			</div>
		</nav>
	</header>		
</div>

    </div>
    <div class="mid-col" q-class="show:isShow,hide:isShow|isFalse">
      
<nav id="mobile-nav">
  	<div class="overlay js-overlay" style="background: #4d4d4d"></div>
	<div class="btnctn js-mobile-btnctn">
  		<div class="slider-trigger list" q-on="click: openSlider(e)"><i class="icon icon-sort"></i></div>
	</div>
	<div class="intrude-less">
		<header id="header" class="inner">
			<div class="profilepic">
				<img src="/img/yinshi.png" class="js-avatar">
			</div>
			<hgroup>
			  <h1 class="header-author js-header-author"></h1>
			</hgroup>
			
			
			
				
			
				
			
			
			
			<nav class="header-nav">
				<div class="social">
					
						<a class="github" target="_blank" href="#" title="github"><i class="icon-github"></i></a>
			        
						<a class="csdn" target="_blank" href="https://blog.csdn.net/qq_35131503" title="csdn"><i class="icon-csdn"></i></a>
			        
				</div>
			</nav>

			<nav class="header-menu js-header-menu">
				<ul style="width: 50%">
				
				
					<li style="width: 50%"><a href="/">主页</a></li>
		        
					<li style="width: 50%"><a href="/archives">归档</a></li>
		        
				</ul>
			</nav>
		</header>				
	</div>
	<div class="mobile-mask" style="display:none" q-show="isShow"></div>
</nav>

      <div id="wrapper" class="body-wrap">
        <div class="menu-l">
          <div class="canvas-wrap">
            <canvas data-colors="#eaeaea" data-sectionHeight="100" data-contentId="js-content" id="myCanvas1" class="anm-canvas"></canvas>
          </div>
          <div id="js-content" class="content-ll">
            
  
    <article id="post-手写线程池-Cpp版" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/31/%E6%89%8B%E5%86%99%E7%BA%BF%E7%A8%8B%E6%B1%A0-Cpp%E7%89%88/">手写线程池-C++版</a>
    </h1>
  

        
        
<a href="/2021/08/31/%E6%89%8B%E5%86%99%E7%BA%BF%E7%A8%8B%E6%B1%A0-Cpp%E7%89%88/" class="archive-article-date">
  	<time datetime="2021-08-31T15:00:30.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-31</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="重写C-版"><a href="#重写C-版" class="headerlink" title="重写C++版"></a>重写C++版</h2><p>C 语言版的线程池改为 C++ 版，这样代码从使用和感观上都会更简洁一些。<br>从 C 到 C++ 的迁移主要用到了 C++ 三大特性中的封装。<br>线程池按照面向对象的思想进行拆分可以分为两部分：<br>（1）任务队列类<br>（2）线程池类</p>
<h2 id="任务队列"><a href="#任务队列" class="headerlink" title="任务队列"></a>任务队列</h2><h3 id="类声明，TaskQueue-h"><a href="#类声明，TaskQueue-h" class="headerlink" title="类声明，TaskQueue.h"></a>类声明，TaskQueue.h</h3><p>Task 是任务类，有两个成员分别是两个指针 <code>void(*)(void*)</code> 和 <code>void*</code><br>TaskQueue 是任务队列，提供了添加任务、取出任务、获取任务个数的功能。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> callback = <span class="built_in">void</span> (*)(<span class="type">void</span>* arg);</span><br><span class="line"><span class="comment">// 任务结构体</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Task</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">Task</span>&lt;T&gt;()</span><br><span class="line">    &#123;</span><br><span class="line">        function = <span class="literal">nullptr</span>;</span><br><span class="line">        arg = <span class="literal">nullptr</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">Task</span>&lt;T&gt;(callback f, <span class="type">void</span>* arg)</span><br><span class="line">    &#123;</span><br><span class="line">        function = f;</span><br><span class="line">        <span class="keyword">this</span>-&gt;arg = (T*)arg;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    callback function;</span><br><span class="line">    T* arg;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TaskQueue</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TaskQueue</span>();</span><br><span class="line">    ~<span class="built_in">TaskQueue</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 添加任务</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">addTask</span><span class="params">(Task&lt;T&gt; task)</span></span>;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">addTask</span><span class="params">(callback f, <span class="type">void</span>* arg)</span></span>;</span><br><span class="line">    <span class="comment">// 取出一个任务</span></span><br><span class="line">    <span class="function">Task&lt;T&gt; <span class="title">takeTask</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="comment">// 获取当前任务的个数</span></span><br><span class="line">    <span class="function"><span class="keyword">inline</span> <span class="type">int</span> <span class="title">taskNumber</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> m_taskQ.<span class="built_in">size</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    queue&lt;Task&lt;T&gt;&gt; m_taskQ;  <span class="comment">// 任务队列</span></span><br><span class="line">    <span class="type">pthread_mutex_t</span> m_mutex; <span class="comment">// 互斥锁</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="类定义，TaskQueue-cpp"><a href="#类定义，TaskQueue-cpp" class="headerlink" title="类定义，TaskQueue.cpp"></a>类定义，TaskQueue.cpp</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">TaskQueue&lt;T&gt;::<span class="built_in">TaskQueue</span>()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">pthread_mutex_init</span>(&amp;m_mutex, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">TaskQueue&lt;T&gt;::~<span class="built_in">TaskQueue</span>() </span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">pthread_mutex_destroy</span>(&amp;m_mutex);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">void</span> TaskQueue&lt;T&gt;::<span class="built_in">addTask</span>(Task&lt;T&gt; task)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">pthread_mutex_lock</span>(&amp;m_mutex);</span><br><span class="line">    m_taskQ.<span class="built_in">push</span>(task);</span><br><span class="line">    <span class="built_in">pthread_mutex_unlock</span>(&amp;m_mutex);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">void</span> TaskQueue&lt;T&gt;::<span class="built_in">addTask</span>(callback f, <span class="type">void</span>* arg) </span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">pthread_mutex_lock</span>(&amp;m_mutex);</span><br><span class="line">    m_taskQ.<span class="built_in">push</span>(<span class="built_in">Task</span>&lt;T&gt;(f, arg));</span><br><span class="line">    <span class="built_in">pthread_mutex_unlock</span>(&amp;m_mutex);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">Task&lt;T&gt; TaskQueue&lt;T&gt;::<span class="built_in">takeTask</span>() </span><br><span class="line">&#123;</span><br><span class="line">    Task&lt;T&gt; t;</span><br><span class="line">    <span class="built_in">pthread_mutex_lock</span>(&amp;m_mutex);</span><br><span class="line">    <span class="keyword">if</span> (!m_taskQ.<span class="built_in">empty</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        t = m_taskQ.<span class="built_in">front</span>();</span><br><span class="line">        m_taskQ.<span class="built_in">pop</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">pthread_mutex_unlock</span>(&amp;m_mutex);</span><br><span class="line">    <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="线程池"><a href="#线程池" class="headerlink" title="线程池"></a>线程池</h2><h3 id="类声明，ThreadPool-h"><a href="#类声明，ThreadPool-h" class="headerlink" title="类声明，ThreadPool.h"></a>类声明，ThreadPool.h</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ThreadPool</span> </span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 创建线程池并初始化</span></span><br><span class="line">    <span class="built_in">ThreadPool</span>(<span class="type">int</span> min, <span class="type">int</span> max);</span><br><span class="line">    <span class="comment">// 销毁线程池</span></span><br><span class="line">    ~<span class="built_in">ThreadPool</span>();</span><br><span class="line">    <span class="comment">// 给线程池添加任务</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">addTask</span><span class="params">(Task&lt;T&gt; task)</span></span>;</span><br><span class="line">    <span class="comment">// 获取线程池中工作的线程的个数</span></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">getBusyNum</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="comment">// 获取线程池中活着的线程的个数</span></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">getAliveNum</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 工作的线程（消费者线程）任务函数</span></span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">void</span>* <span class="title">worker</span><span class="params">(<span class="type">void</span>* arg)</span></span>;</span><br><span class="line">    <span class="comment">// 管理者线程任务函数</span></span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">void</span>* <span class="title">manager</span><span class="params">(<span class="type">void</span>* arg)</span></span>;</span><br><span class="line">    <span class="comment">// 单个线程退出</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">threadExit</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 任务队列</span></span><br><span class="line">    TaskQueue&lt;T&gt;* taskQ;</span><br><span class="line"></span><br><span class="line">    <span class="type">pthread_t</span> managerID;        <span class="comment">// 管理者线程ID</span></span><br><span class="line">    <span class="type">pthread_t</span> *threadIDs;       <span class="comment">// 工作的线程ID</span></span><br><span class="line">    <span class="type">int</span> minNum;                 <span class="comment">// 最小线程数量</span></span><br><span class="line">    <span class="type">int</span> maxNum;                 <span class="comment">// 最大线程数量</span></span><br><span class="line">    <span class="type">int</span> busyNum;                <span class="comment">// 忙的线程的个数</span></span><br><span class="line">    <span class="type">int</span> liveNum;                <span class="comment">// 存活的线程的个数</span></span><br><span class="line">    <span class="type">int</span> exitNum;                <span class="comment">// 要销毁的线程个数</span></span><br><span class="line">    <span class="type">pthread_mutex_t</span> mutexPool;  <span class="comment">// 锁整个的线程池</span></span><br><span class="line">    <span class="type">pthread_cond_t</span> notEmpty;    <span class="comment">// 任务队列是不是空了</span></span><br><span class="line">    <span class="type">bool</span> shutdown;              <span class="comment">// 是否销毁线程池（销毁为1，不销毁为0）</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="类定义，ThreadPool-cpp"><a href="#类定义，ThreadPool-cpp" class="headerlink" title="类定义，ThreadPool.cpp"></a>类定义，ThreadPool.cpp</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">ThreadPool&lt;T&gt;::<span class="built_in">ThreadPool</span>(<span class="type">int</span> min, <span class="type">int</span> max)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 实例化任务队列</span></span><br><span class="line">        taskQ = <span class="keyword">new</span> TaskQueue&lt;T&gt;;</span><br><span class="line">        <span class="keyword">if</span> (taskQ == <span class="literal">nullptr</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;new taskQ fail...\n&quot;</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        threadIDs = <span class="keyword">new</span> <span class="type">pthread_t</span>[max];</span><br><span class="line">        <span class="keyword">if</span> (threadIDs == <span class="literal">nullptr</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;new threadIDs fail...\n&quot;</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">memset</span>(threadIDs, <span class="number">0</span>, <span class="built_in">sizeof</span>(<span class="type">pthread_t</span>) * max);</span><br><span class="line">        minNum = min;</span><br><span class="line">        maxNum = max;</span><br><span class="line">        busyNum = <span class="number">0</span>;</span><br><span class="line">        liveNum = min; <span class="comment">// 等于最小个数</span></span><br><span class="line">        exitNum = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 初始化互斥锁、条件变量</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">pthread_mutex_init</span>(&amp;mutexPool, <span class="literal">NULL</span>) != <span class="number">0</span> ||</span><br><span class="line">            <span class="built_in">pthread_cond_init</span>(&amp;notEmpty, <span class="literal">NULL</span>) != <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;mutex or condition init fail...\n&quot;</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        shutdown = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建线程</span></span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;managerID, <span class="literal">NULL</span>, manager, <span class="keyword">this</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; min; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">pthread_create</span>(&amp;threadIDs[i], <span class="literal">NULL</span>, worker, <span class="keyword">this</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125; <span class="keyword">while</span> (<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放资源</span></span><br><span class="line">    <span class="keyword">if</span> (threadIDs) <span class="keyword">delete</span>[] threadIDs;</span><br><span class="line">    <span class="keyword">if</span> (taskQ) <span class="keyword">delete</span> taskQ;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">ThreadPool&lt;T&gt;::~<span class="built_in">ThreadPool</span>()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 关闭线程池</span></span><br><span class="line">    shutdown = <span class="literal">true</span>;</span><br><span class="line">    <span class="comment">// 阻塞回收管理者线程</span></span><br><span class="line">    <span class="built_in">pthread_join</span>(managerID, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="comment">// 唤醒阻塞的消费者线程</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; liveNum; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_cond_signal</span>(&amp;notEmpty);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 释放堆内存</span></span><br><span class="line">    <span class="keyword">if</span> (taskQ)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">delete</span> taskQ;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (threadIDs)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">delete</span>[] threadIDs;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">pthread_mutex_destroy</span>(&amp;mutexPool);</span><br><span class="line">    <span class="built_in">pthread_cond_destroy</span>(&amp;notEmpty);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">void</span> ThreadPool&lt;T&gt;::<span class="built_in">addTask</span>(Task&lt;T&gt; task) </span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (shutdown)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 添加任务</span></span><br><span class="line">    taskQ-&gt;<span class="built_in">addTask</span>(task);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">pthread_cond_signal</span>(&amp;notEmpty);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">int</span> ThreadPool&lt;T&gt;::<span class="built_in">getBusyNum</span>()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">pthread_mutex_lock</span>(&amp;mutexPool);</span><br><span class="line">    <span class="type">int</span> busyNum = <span class="keyword">this</span>-&gt;busyNum;</span><br><span class="line">    <span class="built_in">pthread_mutex_unlock</span>(&amp;mutexPool);</span><br><span class="line">    <span class="keyword">return</span> busyNum;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">int</span> ThreadPool&lt;T&gt;::<span class="built_in">getAliveNum</span>()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">pthread_mutex_lock</span>(&amp;mutexPool);</span><br><span class="line">    <span class="type">int</span> aliveNum = <span class="keyword">this</span>-&gt;liveNum;</span><br><span class="line">    <span class="built_in">pthread_mutex_unlock</span>(&amp;mutexPool);</span><br><span class="line">    <span class="keyword">return</span> aliveNum;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">void</span>* ThreadPool&lt;T&gt;::<span class="built_in">worker</span>(<span class="type">void</span>* arg)</span><br><span class="line">&#123;</span><br><span class="line">    ThreadPool* pool = <span class="built_in">static_cast</span>&lt;ThreadPool*&gt;(arg);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 一直不停地工作</span></span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">        <span class="comment">// 当前任务队列是否为空</span></span><br><span class="line">        <span class="keyword">while</span> (pool-&gt;taskQ-&gt;<span class="built_in">taskNumber</span>() == <span class="number">0</span> &amp;&amp; !pool-&gt;shutdown)</span><br><span class="line">        &#123;   </span><br><span class="line">            <span class="comment">// 阻塞工作线程</span></span><br><span class="line">            <span class="built_in">pthread_cond_wait</span>(&amp;pool-&gt;notEmpty, &amp;pool-&gt;mutexPool);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解除阻塞之后, 判断是否要销毁线程</span></span><br><span class="line">            <span class="keyword">if</span> (pool-&gt;exitNum &gt; <span class="number">0</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                pool-&gt;exitNum--;</span><br><span class="line">                <span class="keyword">if</span> (pool-&gt;liveNum &gt; pool-&gt;minNum)</span><br><span class="line">                &#123;</span><br><span class="line">                    pool-&gt;liveNum--;</span><br><span class="line">                    <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">                    pool-&gt;<span class="built_in">threadExit</span>();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断线程池是否被关闭</span></span><br><span class="line">        <span class="keyword">if</span> (pool-&gt;shutdown)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">            pool-&gt;<span class="built_in">threadExit</span>();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 从任务队列中取一个任务</span></span><br><span class="line">        Task&lt;T&gt; task = pool-&gt;taskQ-&gt;<span class="built_in">takeTask</span>();</span><br><span class="line">        <span class="comment">// 解锁</span></span><br><span class="line">        pool-&gt;busyNum++;</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 执行任务</span></span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;thread &quot;</span> &lt;&lt; <span class="built_in">to_string</span>(<span class="built_in">pthread_self</span>()) &lt;&lt; <span class="string">&quot; start working...\n&quot;</span>;</span><br><span class="line">        task.<span class="built_in">function</span>(task.arg);</span><br><span class="line">        <span class="keyword">delete</span> task.arg;</span><br><span class="line">        task.arg = <span class="literal">nullptr</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 任务处理结束</span></span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;thread &quot;</span> &lt;&lt; <span class="built_in">to_string</span>(<span class="built_in">pthread_self</span>()) &lt;&lt; <span class="string">&quot; end working...\n&quot;</span>;</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">        pool-&gt;busyNum--;</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">void</span>* ThreadPool&lt;T&gt;::<span class="built_in">manager</span>(<span class="type">void</span>* arg)</span><br><span class="line">&#123;</span><br><span class="line">    ThreadPool* pool = <span class="built_in">static_cast</span>&lt;ThreadPool*&gt;(arg);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (!pool-&gt;shutdown)</span><br><span class="line">    &#123;   </span><br><span class="line">        <span class="comment">// 每隔3s检测一次</span></span><br><span class="line">        <span class="built_in">sleep</span>(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 取出线程池中任务的数量和当前线程的数量</span></span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">        <span class="type">int</span> queueSize = pool-&gt;taskQ-&gt;<span class="built_in">taskNumber</span>();</span><br><span class="line">        <span class="type">int</span> liveNum = pool-&gt;liveNum;</span><br><span class="line">        <span class="comment">// 取出忙的线程的数量</span></span><br><span class="line">        <span class="type">int</span> busyNum = pool-&gt;busyNum;</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line"></span><br><span class="line">        <span class="type">const</span> <span class="type">int</span> NUMBER = <span class="number">2</span>;</span><br><span class="line">        <span class="comment">// 添加线程</span></span><br><span class="line">        <span class="comment">// 任务的个数 &gt; 存活的线程数 &amp;&amp; 存活的线程数 &lt; 最大线程数</span></span><br><span class="line">        <span class="keyword">if</span> (queueSize &gt; liveNum &amp;&amp; liveNum &lt; pool-&gt;maxNum)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">            <span class="type">int</span> counter = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; pool-&gt;maxNum &amp;&amp; counter &lt; NUMBER</span><br><span class="line">                &amp;&amp; pool-&gt;liveNum &lt; pool-&gt;maxNum; ++i)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span> (pool-&gt;threadIDs[i] == <span class="number">0</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="built_in">pthread_create</span>(&amp;pool-&gt;threadIDs[i], <span class="literal">NULL</span>, worker, pool);</span><br><span class="line">                    counter++;</span><br><span class="line">                    pool-&gt;liveNum++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 销毁线程</span></span><br><span class="line">        <span class="comment">// 忙的线程*2 &lt; 存活的线程数 &amp;&amp; 存活的线程数 &gt; 最小线程数</span></span><br><span class="line">        <span class="keyword">if</span> (busyNum * <span class="number">2</span> &lt; liveNum &amp;&amp; liveNum &gt; pool-&gt;minNum)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">            pool-&gt;exitNum = NUMBER;</span><br><span class="line">            <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">            <span class="comment">// 让工作的线程自杀</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; NUMBER; ++i)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="built_in">pthread_cond_signal</span>(&amp;pool-&gt;notEmpty);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">void</span> ThreadPool&lt;T&gt;::<span class="built_in">threadExit</span>()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">pthread_t</span> tid = <span class="built_in">pthread_self</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; maxNum; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (threadIDs[i] == tid)</span><br><span class="line">        &#123;</span><br><span class="line">            threadIDs[i] = <span class="number">0</span>;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;threadExit() called, &quot;</span> &lt;&lt; <span class="built_in">to_string</span>(tid) &lt;&lt; <span class="string">&quot; exiting...\n&quot;</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">pthread_exit</span>(<span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="测试代码"><a href="#测试代码" class="headerlink" title="测试代码"></a>测试代码</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;ThreadPool.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;ThreadPool.cpp&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">taskFunc</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> num = *(<span class="type">int</span>*)arg;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;thread &quot;</span> &lt;&lt; <span class="built_in">to_string</span>(<span class="built_in">pthread_self</span>()) &lt;&lt; <span class="string">&quot;is working, number = &quot;</span> &lt;&lt; num &lt;&lt; <span class="string">&quot;\n&quot;</span>;</span><br><span class="line">    <span class="built_in">sleep</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 创建线程池</span></span><br><span class="line">    <span class="function">ThreadPool&lt;<span class="type">int</span>&gt; <span class="title">pool</span><span class="params">(<span class="number">3</span>, <span class="number">10</span>)</span></span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">int</span>* num = <span class="keyword">new</span> <span class="built_in">int</span>(i + <span class="number">100</span>);</span><br><span class="line">        pool.<span class="built_in">addTask</span>(<span class="built_in">Task</span>&lt;<span class="type">int</span>&gt;(taskFunc, num));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 主线程睡眠一段时间，保证工作线程处理完毕</span></span><br><span class="line">    <span class="built_in">sleep</span>(<span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ test.cpp -lpthread -o test</span></span><br></pre></td></tr></table></figure>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">线程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/31/%E6%89%8B%E5%86%99%E7%BA%BF%E7%A8%8B%E6%B1%A0-Cpp%E7%89%88/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-手写线程池-C语言版" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/31/%E6%89%8B%E5%86%99%E7%BA%BF%E7%A8%8B%E6%B1%A0-C%E8%AF%AD%E8%A8%80%E7%89%88/">手写线程池-C语言版</a>
    </h1>
  

        
        
<a href="/2021/08/31/%E6%89%8B%E5%86%99%E7%BA%BF%E7%A8%8B%E6%B1%A0-C%E8%AF%AD%E8%A8%80%E7%89%88/" class="archive-article-date">
  	<time datetime="2021-08-31T14:05:35.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-31</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="线程池原理"><a href="#线程池原理" class="headerlink" title="线程池原理"></a>线程池原理</h2><p>使用线程的时候就去创建一个线程，这样实现起来非常简便，但是就会有一个问题：<br>如果并发的线程数量很多，并且每个线程都是执行一个时间很短的任务就结束了，<br>这样频繁创建线程就会大大降低系统的效率，因为创建线程和销毁线程需要时间。</p>
<p>有没有一种办法使得线程可以复用，就是执行完一个任务，并不被销毁，而是继续执行其他的任务呢？<br>线程池是一种多线程处理形式，处理过程中将任务添加到队列，然后在创建线程后自动启动这些任务。<br>线程池线程都是后台线程。每个线程都使用默认的堆栈大小，以默认的优先级运行，并处于多线程单元中。</p>
<p>线程池的组成主要分为 3 个部分：<br>（1）任务队列：存储需要处理的任务，由工作的线程来处理这些任务。<br>通过线程池提供的 API 函数，将一个待处理的任务添加到任务队列，或者从任务队列中删除。<br>已处理的任务会被从任务队列中删除。<br>线程池的使用者，调用线程池函数往任务队列中添加任务的线程就是生产者线程。<br>（2）工作的线程（任务队列中任务的消费者，N 个）<br>线程池中维护了一定数量的工作线程，它的作用是不停地读任务队列，从中取出任务并处理。<br>工作的线程相当于是任务队列的消费者角色，<br>    如果任务队列为空，工作的线程将会被阻塞 (使用条件变量/信号量阻塞)；<br>    如果阻塞之后有了新的任务，由生产者将阻塞解除，工作线程开始工作。<br>（3）管理者线程（不处理任务队列中的任务，1 个）<br>它的作用是周期性的检测任务队列中的任务数量以及处于忙状态的工作线程个数，<br>    当任务过多的时候，可以适当的创建一些新的工作线程；<br>    当任务过少的时候，可以适当的销毁一些工作的线程。</p>
<h2 id="任务队列"><a href="#任务队列" class="headerlink" title="任务队列"></a>任务队列</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 任务结构体</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">Task</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">void</span> (*function)(<span class="type">void</span>* arg);</span><br><span class="line">    <span class="type">void</span>* arg;</span><br><span class="line">&#125;Task;</span><br></pre></td></tr></table></figure>

<h2 id="线程池定义"><a href="#线程池定义" class="headerlink" title="线程池定义"></a>线程池定义</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 线程池结构体</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">ThreadPool</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 任务队列</span></span><br><span class="line">    Task* taskQ;</span><br><span class="line">    <span class="type">int</span> queueCapacity;  <span class="comment">// 容量</span></span><br><span class="line">    <span class="type">int</span> queueSize;      <span class="comment">// 当前任务个数</span></span><br><span class="line">    <span class="type">int</span> queueFront;     <span class="comment">// 队头 -&gt; 取数据</span></span><br><span class="line">    <span class="type">int</span> queueRear;      <span class="comment">// 队尾 -&gt; 放数据</span></span><br><span class="line"></span><br><span class="line">    <span class="type">pthread_t</span> managerID;    <span class="comment">// 管理者线程ID</span></span><br><span class="line">    <span class="type">pthread_t</span> *threadIDs;   <span class="comment">// 工作的线程ID</span></span><br><span class="line">    <span class="type">int</span> minNum;             <span class="comment">// 最小线程数量</span></span><br><span class="line">    <span class="type">int</span> maxNum;             <span class="comment">// 最大线程数量</span></span><br><span class="line">    <span class="type">int</span> busyNum;            <span class="comment">// 忙的线程的个数</span></span><br><span class="line">    <span class="type">int</span> liveNum;            <span class="comment">// 存活的线程的个数</span></span><br><span class="line">    <span class="type">int</span> exitNum;            <span class="comment">// 要销毁的线程个数</span></span><br><span class="line">    <span class="type">pthread_mutex_t</span> mutexPool;  <span class="comment">// 锁整个的线程池</span></span><br><span class="line">    <span class="type">pthread_mutex_t</span> mutexBusy;  <span class="comment">// 锁busyNum变量</span></span><br><span class="line">    <span class="type">pthread_cond_t</span> notFull;     <span class="comment">// 任务队列是不是满了</span></span><br><span class="line">    <span class="type">pthread_cond_t</span> notEmpty;    <span class="comment">// 任务队列是不是空了</span></span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> shutdown;           <span class="comment">// 是不是要销毁线程池, 销毁为1, 不销毁为0</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="头文件声明，ThreadPool-h"><a href="#头文件声明，ThreadPool-h" class="headerlink" title="头文件声明，ThreadPool.h"></a>头文件声明，ThreadPool.h</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _THREADPOOL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _THREADPOOL_H</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">ThreadPool</span> ThreadPool;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建线程池并初始化</span></span><br><span class="line"><span class="function">ThreadPool* <span class="title">threadPoolCreate</span><span class="params">(<span class="type">int</span> min, <span class="type">int</span> max, <span class="type">int</span> queueSize)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 销毁线程池</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">threadPoolDestroy</span><span class="params">(ThreadPool* pool)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 给线程池添加任务</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">threadPoolAdd</span><span class="params">(ThreadPool* pool, <span class="type">void</span>(*func)(<span class="type">void</span>*), <span class="type">void</span>* arg)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取线程池中工作的线程的个数</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">threadPoolBusyNum</span><span class="params">(ThreadPool* pool)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取线程池中活着的线程的个数</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">threadPoolAliveNum</span><span class="params">(ThreadPool* pool)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 工作的线程（消费者线程）任务函数</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">worker</span><span class="params">(<span class="type">void</span>* arg)</span></span>;</span><br><span class="line"><span class="comment">// 管理者线程任务函数</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">manager</span><span class="params">(<span class="type">void</span>* arg)</span></span>;</span><br><span class="line"><span class="comment">// 单个线程退出</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">threadExit</span><span class="params">(ThreadPool* pool)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// _THREADPOOL_H</span></span></span><br></pre></td></tr></table></figure>

<h2 id="源文件定义，ThreadPool-c"><a href="#源文件定义，ThreadPool-c" class="headerlink" title="源文件定义，ThreadPool.c"></a>源文件定义，ThreadPool.c</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function">ThreadPool* <span class="title">threadPoolCreate</span><span class="params">(<span class="type">int</span> min, <span class="type">int</span> max, <span class="type">int</span> queueSize)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ThreadPool* pool = (ThreadPool*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(ThreadPool));</span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (pool == <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;malloc threadpool fail...\n&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        pool-&gt;threadIDs = (<span class="type">pthread_t</span>*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">pthread_t</span>) * max);</span><br><span class="line">        <span class="keyword">if</span> (pool-&gt;threadIDs == <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;malloc threadIDs fail...\n&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">memset</span>(pool-&gt;threadIDs, <span class="number">0</span>, <span class="built_in">sizeof</span>(<span class="type">pthread_t</span>) * max);</span><br><span class="line">        pool-&gt;minNum = min;</span><br><span class="line">        pool-&gt;maxNum = max;</span><br><span class="line">        pool-&gt;busyNum = <span class="number">0</span>;</span><br><span class="line">        pool-&gt;liveNum = min; <span class="comment">// 等于最小个数</span></span><br><span class="line">        pool-&gt;exitNum = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">pthread_mutex_init</span>(&amp;pool-&gt;mutexPool, <span class="literal">NULL</span>) != <span class="number">0</span> ||</span><br><span class="line">            <span class="built_in">pthread_mutex_init</span>(&amp;pool-&gt;mutexBusy, <span class="literal">NULL</span>) != <span class="number">0</span> ||</span><br><span class="line">            <span class="built_in">pthread_cond_init</span>(&amp;pool-&gt;notFull, <span class="literal">NULL</span>) != <span class="number">0</span> ||</span><br><span class="line">            <span class="built_in">pthread_cond_init</span>(&amp;pool-&gt;notEmpty, <span class="literal">NULL</span>) != <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;mutex or condition init fail...\n&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 任务队列</span></span><br><span class="line">        pool-&gt;taskQ = (Task*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(Task) * queueSize);</span><br><span class="line">        pool-&gt;queueCapacity = queueSize;</span><br><span class="line">        pool-&gt;queueSize = <span class="number">0</span>;</span><br><span class="line">        pool-&gt;queueFront = <span class="number">0</span>;</span><br><span class="line">        pool-&gt;queueRear = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        pool-&gt;shutdown = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建线程</span></span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;pool-&gt;managerID, <span class="literal">NULL</span>, manager, pool);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; min; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">pthread_create</span>(&amp;pool-&gt;threadIDs[i], <span class="literal">NULL</span>, worker, pool);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> pool;</span><br><span class="line">    &#125; <span class="keyword">while</span> (<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放资源</span></span><br><span class="line">    <span class="keyword">if</span> (pool &amp;&amp; pool-&gt;threadIDs) <span class="built_in">free</span>(pool-&gt;threadIDs);</span><br><span class="line">    <span class="keyword">if</span> (pool &amp;&amp; pool-&gt;taskQ) <span class="built_in">free</span>(pool-&gt;taskQ);</span><br><span class="line">    <span class="keyword">if</span> (pool) <span class="built_in">free</span>(pool);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">threadPoolDestroy</span><span class="params">(ThreadPool* pool)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (pool == <span class="literal">NULL</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭线程池</span></span><br><span class="line">    pool-&gt;shutdown = <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 阻塞回收管理者线程</span></span><br><span class="line">    <span class="built_in">pthread_join</span>(pool-&gt;managerID, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="comment">// 唤醒阻塞的消费者线程</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; pool-&gt;liveNum; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_cond_signal</span>(&amp;pool-&gt;notEmpty);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 释放堆内存</span></span><br><span class="line">    <span class="keyword">if</span> (pool-&gt;taskQ)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">free</span>(pool-&gt;taskQ);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (pool-&gt;threadIDs)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">free</span>(pool-&gt;threadIDs);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">pthread_mutex_destroy</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">    <span class="built_in">pthread_mutex_destroy</span>(&amp;pool-&gt;mutexBusy);</span><br><span class="line">    <span class="built_in">pthread_cond_destroy</span>(&amp;pool-&gt;notEmpty);</span><br><span class="line">    <span class="built_in">pthread_cond_destroy</span>(&amp;pool-&gt;notFull);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">free</span>(pool);</span><br><span class="line">    pool = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">threadPoolAdd</span><span class="params">(ThreadPool* pool, <span class="type">void</span>(*func)(<span class="type">void</span>*), <span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">    <span class="keyword">while</span> (pool-&gt;queueSize == pool-&gt;queueCapacity &amp;&amp; !pool-&gt;shutdown)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 阻塞生产者线程</span></span><br><span class="line">        <span class="built_in">pthread_cond_wait</span>(&amp;pool-&gt;notFull, &amp;pool-&gt;mutexPool);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (pool-&gt;shutdown)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 添加任务</span></span><br><span class="line">    pool-&gt;taskQ[pool-&gt;queueRear].function = func;</span><br><span class="line">    pool-&gt;taskQ[pool-&gt;queueRear].arg = arg;</span><br><span class="line">    pool-&gt;queueRear = (pool-&gt;queueRear + <span class="number">1</span>) % pool-&gt;queueCapacity;</span><br><span class="line">    pool-&gt;queueSize++;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">pthread_cond_signal</span>(&amp;pool-&gt;notEmpty);</span><br><span class="line">    <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">threadPoolBusyNum</span><span class="params">(ThreadPool* pool)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexBusy);</span><br><span class="line">    <span class="type">int</span> busyNum = pool-&gt;busyNum;</span><br><span class="line">    <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexBusy);</span><br><span class="line">    <span class="keyword">return</span> busyNum;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">threadPoolAliveNum</span><span class="params">(ThreadPool* pool)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">    <span class="type">int</span> aliveNum = pool-&gt;liveNum;</span><br><span class="line">    <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">    <span class="keyword">return</span> aliveNum;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">worker</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ThreadPool* pool = (ThreadPool*) arg;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">        <span class="comment">// 当前任务队列是否为空</span></span><br><span class="line">        <span class="keyword">while</span> (pool-&gt;queueSize == <span class="number">0</span> &amp;&amp; !pool-&gt;shutdown)</span><br><span class="line">        &#123;   </span><br><span class="line">            <span class="comment">// 阻塞工作线程</span></span><br><span class="line">            <span class="built_in">pthread_cond_wait</span>(&amp;pool-&gt;notEmpty, &amp;pool-&gt;mutexPool);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 判断是不是要销毁线程</span></span><br><span class="line">            <span class="keyword">if</span> (pool-&gt;exitNum &gt; <span class="number">0</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                pool-&gt;exitNum--;</span><br><span class="line">                <span class="keyword">if</span> (pool-&gt;liveNum &gt; pool-&gt;minNum)</span><br><span class="line">                &#123;</span><br><span class="line">                    pool-&gt;liveNum--;</span><br><span class="line">                    <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">                    <span class="built_in">threadExit</span>(pool);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断线程池是否被关闭了</span></span><br><span class="line">        <span class="keyword">if</span> (pool-&gt;shutdown)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">            <span class="built_in">threadExit</span>(pool);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 从任务队列中取一个任务</span></span><br><span class="line">        Task task;</span><br><span class="line">        task.function = pool-&gt;taskQ[pool-&gt;queueFront].function;</span><br><span class="line">        task.arg = pool-&gt;taskQ[pool-&gt;queueFront].arg;</span><br><span class="line">        <span class="comment">// 移动头节点</span></span><br><span class="line">        pool-&gt;queueFront = (pool-&gt;queueFront + <span class="number">1</span>) % pool-&gt;queueCapacity;</span><br><span class="line">        pool-&gt;queueSize--;</span><br><span class="line">        <span class="comment">// 解锁</span></span><br><span class="line">        <span class="built_in">pthread_cond_signal</span>(&amp;pool-&gt;notFull);</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;thread %ld start working...\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexBusy);</span><br><span class="line">        pool-&gt;busyNum++;</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexBusy);</span><br><span class="line">        task.<span class="built_in">function</span>(task.arg);</span><br><span class="line">        <span class="built_in">free</span>(task.arg);</span><br><span class="line">        task.arg = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;thread %ld end working...\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexBusy);</span><br><span class="line">        pool-&gt;busyNum--;</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexBusy);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">manager</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ThreadPool* pool = (ThreadPool*) arg;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (!pool-&gt;shutdown)</span><br><span class="line">    &#123;   </span><br><span class="line">        <span class="comment">// 每隔3s检测一次</span></span><br><span class="line">        <span class="built_in">sleep</span>(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 取出线程池中任务的数量和当前线程的数量</span></span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">        <span class="type">int</span> queueSize = pool-&gt;queueSize;</span><br><span class="line">        <span class="type">int</span> liveNum = pool-&gt;liveNum;</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 取出忙的线程的数量</span></span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexBusy);</span><br><span class="line">        <span class="type">int</span> busyNum = pool-&gt;busyNum;</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexBusy);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 添加线程</span></span><br><span class="line">        <span class="comment">// 任务的个数 &gt; 存活的线程数 &amp;&amp; 存活的线程数 &lt; 最大线程数</span></span><br><span class="line">        <span class="keyword">if</span> (queueSize &gt; liveNum &amp;&amp; liveNum &lt; pool-&gt;maxNum)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">            <span class="type">int</span> counter = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; pool-&gt;maxNum &amp;&amp; counter &lt; NUMBER</span><br><span class="line">                &amp;&amp; pool-&gt;liveNum &lt; pool-&gt;maxNum; ++i)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span> (pool-&gt;threadIDs[i] == <span class="number">0</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="built_in">pthread_create</span>(&amp;pool-&gt;threadIDs[i], <span class="literal">NULL</span>, worker, pool);</span><br><span class="line">                    counter++;</span><br><span class="line">                    pool-&gt;liveNum++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 销毁线程</span></span><br><span class="line">        <span class="comment">// 忙的线程*2 &lt; 存活的线程数 &amp;&amp; 存活的线程数 &gt; 最小线程数</span></span><br><span class="line">        <span class="keyword">if</span> (busyNum * <span class="number">2</span> &lt; liveNum &amp;&amp; liveNum &gt; pool-&gt;minNum)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">pthread_mutex_lock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">            pool-&gt;exitNum = NUMBER;</span><br><span class="line">            <span class="built_in">pthread_mutex_unlock</span>(&amp;pool-&gt;mutexPool);</span><br><span class="line">            <span class="comment">// 让工作的线程自杀</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; NUMBER; ++i)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="built_in">pthread_cond_signal</span>(&amp;pool-&gt;notEmpty);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">threadExit</span><span class="params">(ThreadPool* pool)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">pthread_t</span> tid = <span class="built_in">pthread_self</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; pool-&gt;maxNum; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (pool-&gt;threadIDs[i] == tid)</span><br><span class="line">        &#123;</span><br><span class="line">            pool-&gt;threadIDs[i] = <span class="number">0</span>;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;threadExit() called, %ld exiting...\n&quot;</span>, tid);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">pthread_exit</span>(<span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="测试代码"><a href="#测试代码" class="headerlink" title="测试代码"></a>测试代码</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;ThreadPool.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">taskFunc</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> num = *(<span class="type">int</span>*)arg;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;thread %ld is working, number = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), num);</span><br><span class="line">    <span class="built_in">sleep</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 创建线程池</span></span><br><span class="line">    ThreadPool* pool = <span class="built_in">threadPoolCreate</span>(<span class="number">3</span>, <span class="number">10</span>, <span class="number">100</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">int</span>* num = (<span class="type">int</span>*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">int</span>));</span><br><span class="line">        *num = i + <span class="number">100</span>;</span><br><span class="line">        <span class="built_in">threadPoolAdd</span>(pool, taskFunc, num);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 主线程睡眠一段时间，保证工作线程处理完毕</span></span><br><span class="line">    <span class="built_in">sleep</span>(<span class="number">30</span>);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">threadPoolDestroy</span>(pool);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// gcc main.c ThreadPool.c -o main -lpthread</span></span><br></pre></td></tr></table></figure>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">线程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/31/%E6%89%8B%E5%86%99%E7%BA%BF%E7%A8%8B%E6%B1%A0-C%E8%AF%AD%E8%A8%80%E7%89%88/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-Cpp原子变量" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/28/Cpp%E5%8E%9F%E5%AD%90%E5%8F%98%E9%87%8F/">C++原子变量</a>
    </h1>
  

        
        
<a href="/2021/08/28/Cpp%E5%8E%9F%E5%AD%90%E5%8F%98%E9%87%8F/" class="archive-article-date">
  	<time datetime="2021-08-28T00:55:05.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-28</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>C++11 提供了一个原子类型 <code>std::atomic&lt;T&gt;</code>，通过这个原子类型管理的内部变量就可以称为原子变量，可以给原子类型指定任意的类型作为模板参数，因此原子变量可以是任意的类型。</p>
<p>C++11 内置了整形的原子变量，这样就可以更方便地使用原子变量。在多线程操作中，使用原子变量之后就不需要再使用互斥量来保护该变量，使用起来更简洁。因为对原子变量进行的操作只能是一个原子操作 <code>atomic operation</code>，原子操作是不会被线程调度机制打断的操作，这种操作一旦开始，就一直运行到结束，中间不会有任何的上下文切换。多线程同时访问共享资源造成数据混乱是因为 CPU 的上下文切换导致的，使用原子变量解决了这个问题，因此不需要使用互斥锁。</p>
<h2 id="atomic类"><a href="#atomic类" class="headerlink" title="atomic类"></a>atomic类</h2><h3 id="类定义"><a href="#类定义" class="headerlink" title="类定义"></a>类定义</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义于头文件 &lt;atomic&gt;</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">T</span>&gt;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">atomic</span>;</span><br></pre></td></tr></table></figure>
<p>通过定义知道，在使用这个模板类的时候，一定要指定模板类型。</p>
<h3 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="built_in">atomic</span>() <span class="keyword">noexcept</span> = <span class="keyword">default</span>;</span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="function"><span class="keyword">constexpr</span> <span class="title">atomic</span><span class="params">(T desired)</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="comment">// 3</span></span><br><span class="line"><span class="built_in">atomic</span>(<span class="type">const</span> atomic&amp;) = <span class="keyword">delete</span>;</span><br></pre></td></tr></table></figure>
<p>构造函数1：默认无参构造函数。<br>构造函数2：使用 desired 初始化原子变量的值。<br>构造函数3：使用 =delete 删除拷贝构造函数，不允许进行对象之间的拷贝。</p>
<h3 id="公共成员函数"><a href="#公共成员函数" class="headerlink" title="公共成员函数"></a>公共成员函数</h3><p>原子类型在类内部重载了 = 操作符，并且不允许在类的外部使用 = 进行对象的拷贝。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">T <span class="keyword">operator</span>=(T desired) <span class="keyword">noexcept</span>;</span><br><span class="line">T <span class="keyword">operator</span>=(T desired) <span class="keyword">volatile</span> <span class="keyword">noexcept</span>;</span><br><span class="line"></span><br><span class="line">atomic&amp; <span class="keyword">operator</span>=(<span class="type">const</span> atomic&amp;) = <span class="keyword">delete</span>;</span><br><span class="line">atomic&amp; <span class="keyword">operator</span>=(<span class="type">const</span> atomic&amp;) <span class="keyword">volatile</span> = <span class="keyword">delete</span>;</span><br></pre></td></tr></table></figure>

<p>原子地以 desired 替换当前值，按照 order 的值影响内存。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">store</span><span class="params">(T desired, std::memory_order order = std::memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">store</span><span class="params">(T desired, std::memory_order order = std::memory_order_seq_cst)</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    desired：</span></span><br><span class="line"><span class="comment">        存储到原子变量中的值</span></span><br><span class="line"><span class="comment">    order：</span></span><br><span class="line"><span class="comment">        强制的内存顺序</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<p>原子地加载并返回原子变量的当前值，按照 order 的值影响内存。直接访问原子对象也可以得到原子变量的当前值。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">T <span class="title">load</span><span class="params">(std::memory_order order = std::memory_order_seq_cst)</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function">T <span class="title">load</span><span class="params">(std::memory_order order = std::memory_order_seq_cst)</span> <span class="type">const</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<h3 id="C-20-新增成员"><a href="#C-20-新增成员" class="headerlink" title="C++20 新增成员"></a>C++20 新增成员</h3><p>在 C++20 版本中添加了新的功能函数，可以通过原子类型来阻塞线程，和条件变量中的等待/通知函数是一样的。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">wait</span>(C++<span class="number">20</span>)：阻塞线程直至被唤醒且原子值更改</span><br><span class="line"><span class="built_in">notify_one</span>(C++<span class="number">20</span>)：通知（唤醒）至少一个在原子对象上阻塞的线程</span><br><span class="line"><span class="built_in">notify_all</span>(C++<span class="number">20</span>)：通知（唤醒）所有在原子对象上阻塞的线程</span><br></pre></td></tr></table></figure>

<h2 id="原子变量的使用"><a href="#原子变量的使用" class="headerlink" title="原子变量的使用"></a>原子变量的使用</h2><p>一个多线程交替数数的计数器，使用互斥锁和原子变量的方式分别进行实现，比较二者的不同：</p>
<h3 id="互斥锁"><a href="#互斥锁" class="headerlink" title="互斥锁"></a>互斥锁</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Counter</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">increment</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">            m_value++;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;increment number: &quot;</span> &lt;&lt; m_value </span><br><span class="line">                 &lt;&lt; <span class="string">&quot;, threadId: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">microseconds</span>(<span class="number">100</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">decrement</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">            m_value--;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;decrement number: &quot;</span> &lt;&lt; m_value </span><br><span class="line">                 &lt;&lt; <span class="string">&quot;, threadId: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">microseconds</span>(<span class="number">100</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> m_value = <span class="number">0</span>;</span><br><span class="line">    mutex m_mutex;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Counter c;</span><br><span class="line">    <span class="keyword">auto</span> increment = <span class="built_in">bind</span>(&amp;Counter::increment, &amp;c);</span><br><span class="line">    <span class="keyword">auto</span> decrement = <span class="built_in">bind</span>(&amp;Counter::decrement, &amp;c);</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(increment)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(decrement)</span></span>;</span><br><span class="line"></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ lock_guard2.cpp -o app</span></span><br></pre></td></tr></table></figure>

<h3 id="原子变量"><a href="#原子变量" class="headerlink" title="原子变量"></a>原子变量</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Counter</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">Counter</span>() : <span class="built_in">m_value</span>(<span class="number">0</span>) &#123;&#125; </span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">increment</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10000</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            m_value++;</span><br><span class="line">            <span class="comment">// cout &lt;&lt; &quot;increment number: &quot; &lt;&lt; m_value </span></span><br><span class="line">            <span class="comment">//      &lt;&lt; &quot;, threadId: &quot; &lt;&lt; this_thread::get_id() &lt;&lt; endl;</span></span><br><span class="line">            <span class="comment">// this_thread::sleep_for(chrono::milliseconds(500));</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">decrement</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10000</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// m_value--;</span></span><br><span class="line">            <span class="comment">// cout &lt;&lt; &quot;decrement number: &quot; &lt;&lt; m_value </span></span><br><span class="line">            <span class="comment">//      &lt;&lt; &quot;, threadId: &quot; &lt;&lt; this_thread::get_id() &lt;&lt; endl;</span></span><br><span class="line">            <span class="comment">// this_thread::sleep_for(chrono::milliseconds(500));</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// atomic&lt;int&gt; = atomic_int</span></span><br><span class="line">    atomic_int m_value;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Counter c;</span><br><span class="line">    <span class="keyword">auto</span> increment = <span class="built_in">bind</span>(&amp;Counter::increment, &amp;c);</span><br><span class="line">    <span class="keyword">auto</span> decrement = <span class="built_in">bind</span>(&amp;Counter::decrement, &amp;c);</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(increment)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(decrement)</span></span>;</span><br><span class="line">    cout &lt;&lt; c.m_value &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ atomic.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>使用这些原子类型即可摆脱每次对共享变量进行操作都进行的加锁解锁动作，节省了系统开销，同时避免了线程因阻塞而频繁的切换。但是<code>atomic</code>无法做到线程之间的同步。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li>atomic<br><a target="_blank" rel="noopener" href="https://subingwen.cn/cpp/atomic">https://subingwen.cn/cpp/atomic</a></li>
<li>bind<br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/myd620/p/8706651.html">https://www.cnblogs.com/myd620/p/8706651.html</a><br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/jialin0x7c9/p/12219239.html">https://www.cnblogs.com/jialin0x7c9/p/12219239.html</a></li>
<li>无锁编程<br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/lsgxeva/p/11076318.html">https://www.cnblogs.com/lsgxeva/p/11076318.html</a><br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/zl1991/p/12013156.html">https://www.cnblogs.com/zl1991/p/12013156.html</a></li>
</ul>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">线程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/28/Cpp%E5%8E%9F%E5%AD%90%E5%8F%98%E9%87%8F/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-Cpp线程同步" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/27/Cpp%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5/">C++线程同步</a>
    </h1>
  

        
        
<a href="/2021/08/27/Cpp%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5/" class="archive-article-date">
  	<time datetime="2021-08-27T12:05:56.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-27</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="线程同步"><a href="#线程同步" class="headerlink" title="线程同步"></a>线程同步</h2><p>进行多线程编程，如果多个线程需要对同一块内存进行操作，比如：同时读、同时写、同时读写。<br>对于后两种情况来说，如果不做任何的人为干涉就会出现各种各样的错误数据。这是因为线程在运行的时候需要先得到 CPU 时间片，时间片用完之后需要放弃已获得的 CPU 资源，线程频繁地在就绪态和运行态之间切换，更复杂一点还可以在就绪态、运行态、挂起态之间切换，这样就会导致线程的执行顺序并不是有序的，而是随机的、混乱的。</p>
<h2 id="互斥锁"><a href="#互斥锁" class="headerlink" title="互斥锁"></a>互斥锁</h2><p>解决多线程数据混乱的方案就是进行线程同步，最常用的就是互斥锁，C++11 中一共提供了四种互斥锁：<br><code>std::mutex</code>：独占的互斥锁，不能递归使用<br><code>std::timed_mutex</code>：带超时的独占互斥锁，不能递归使用<br><code>std::recursive_mutex</code>：递归互斥锁，不带超时功能<br><code>std::recursive_timed_mutex</code>：递归互斥锁，带超时<br>互斥锁也被称为互斥量，二者是一个东西。</p>
<h3 id="std-mutex"><a href="#std-mutex" class="headerlink" title="std::mutex"></a>std::mutex</h3><p>不论是在 C 还是 C++ 中，进行线程同步的处理流程基本上是一致的，C++ 的 mutex 类提供了相关的 API 函数：</p>
<h4 id="成员函数"><a href="#成员函数" class="headerlink" title="成员函数"></a>成员函数</h4><p><code>lock()</code> 函数用于给临界区加锁，并且只能有一个线程获得锁的所有权，它有阻塞线程的作用，函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">lock</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>
<p>独占互斥锁对象有两种状态：锁定和未锁定。<br>如果互斥锁是打开的，调用 <code>lock()</code> 函数的线程会得到互斥锁的所有权，并将其上锁，其它线程再调用该函数的时候由于得不到互斥锁的所有权，就会被 <code>lock()</code> 函数阻塞。当拥有互斥锁所有权的线程将互斥锁解锁，此时被 <code>lock()</code> 阻塞的线程解除阻塞，抢到互斥锁所有权的线程加锁并继续运行，没抢到互斥锁所有权的线程继续阻塞。</p>
<p>除了使用 <code>lock()</code> 还可以使用 <code>try_lock()</code> 获取互斥锁的所有权并对互斥锁加锁，函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">try_lock</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>
<p>二者的区别在于 <code>try_lock()</code> 不会阻塞线程，<code>lock()</code> 会阻塞线程：<br>（1）如果互斥锁是未锁定状态，得到了互斥锁所有权并加锁成功，函数返回 true；<br>（2）如果互斥锁是锁定状态，无法得到互斥锁所有权加锁失败，函数返回 false。</p>
<p>当互斥锁被锁定之后可以通过 <code>unlock()</code> 进行解锁，但是注意只有拥有互斥锁所有权的线程（对互斥锁上锁的线程才能将其解锁），其它线程是没有权限做这件事情的。函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">unlock</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>
<p>通过介绍以上三个函数，使用互斥锁进行线程同步的大致思路差不多就能搞清楚了，主要分为以下几步：<br>（1）找到多个线程操作的共享资源（全局变量、堆内存、类成员变量等），也可以称为临界资源<br>（2）找到和共享资源有关的上下文代码，也就是临界区<br>（3）在临界区的上面调用互斥锁类的 lock() 方法<br>（4）在临界区的下面调用互斥锁的 unlock() 方法</p>
<p>线程同步的目的是让多线程按照顺序依次执行临界区代码，这样做线程对共享资源的访问就从并行访问变为了线性访问，访问效率降低了，但是保证了数据的正确性。当线程对互斥锁对象加锁，并且执行完临界区代码之后，一定要使用这个线程对互斥锁解锁，否则最终会造成线程的死锁。死锁之后当前应用程序中的所有线程都会被阻塞，并且阻塞无法解除，应用程序也无法继续运行。</p>
<h4 id="线程同步-1"><a href="#线程同步-1" class="headerlink" title="线程同步"></a>线程同步</h4><p>举例：两个线程共同操作同一个全局变量，二者交替数数，将数值存储到这个全局变量并打印。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> g_num = <span class="number">0</span>;</span><br><span class="line">mutex g_num_mutex;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">slow_inc</span><span class="params">(<span class="type">int</span> id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        g_num_mutex.<span class="built_in">lock</span>();</span><br><span class="line">        ++g_num;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;thread: &quot;</span> &lt;&lt; id &lt;&lt; <span class="string">&quot; =&gt; &quot;</span> &lt;&lt; g_num &lt;&lt; endl;</span><br><span class="line">        g_num_mutex.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(slow_inc, <span class="number">0</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(slow_inc, <span class="number">1</span>)</span></span>;</span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ mutex.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>上面的示例代码中，两个子线程执行的任务是一样的（其实也可以不一样，不同的任务中也可以对共享资源进行读写操作），在任务函数中把与全局变量相关的代码加了锁，两个线程只能顺序访问这部分代码（如果不进行线程同步，打印出的数据是混乱且无序的）。<br>另外，注意：在所有线程的任务函数执行完毕之前，互斥锁对象是不能被析构的，一定要在程序中保证这个对象的可用性。互斥锁的个数和共享资源的个数相等，也就是说每一个共享资源都应该对应一个互斥锁对象。互斥锁对象的个数和线程的个数没有关系。</p>
<h3 id="std-lock-guard"><a href="#std-lock-guard" class="headerlink" title="std::lock_guard"></a>std::lock_guard</h3><p><code>lock_guard</code> 是 C++11 新增的一个模板类，使用这个类可以简化互斥锁 <code>lock()</code> 和 <code>unlock() </code>的写法，同时也更安全。<br>这个模板类的定义和常用的构造函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 类的定义，定义于头文件 &lt;mutex&gt;</span></span><br><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">class</span> <span class="title class_">Mutex</span>&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">lock_guard</span>;</span><br><span class="line"><span class="comment">// 常用构造函数</span></span><br><span class="line"><span class="function"><span class="keyword">explicit</span> <span class="title">lock_guard</span><span class="params">(mutex_type&amp; m)</span></span>;</span><br></pre></td></tr></table></figure>
<p><code>lock_guard</code> 在使用上面提供的这个构造函数构造对象时，会自动锁定互斥量，而在退出作用域后进行析构时就会自动解锁，从而保证了互斥量的正确操作，避免忘记 <code>unlock()</code> 操作而导致线程死锁。<code>lock_guard</code> 使用了 <code>RAII</code> 技术，在类构造函数中分配资源，在析构函数中释放资源，保证资源出了作用域就释放。</p>
<p>使用 <code>lock_guard</code> 对上面的示例进行修改，代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> g_num = <span class="number">0</span>;</span><br><span class="line">mutex g_num_mutex;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">slow_inc</span><span class="params">(<span class="type">int</span> id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">6</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">lock</span><span class="params">(g_num_mutex)</span></span>;</span><br><span class="line">        ++g_num;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;thread: &quot;</span> &lt;&lt; id &lt;&lt; <span class="string">&quot; =&gt; &quot;</span> &lt;&lt; g_num &lt;&lt; endl;</span><br><span class="line">        this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(slow_inc, <span class="number">0</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(slow_inc, <span class="number">1</span>)</span></span>;</span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ lock_guard.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>代码被精简，而且不用担心因为忘记解锁而造成程序的死锁，但是这种方式也有弊端。在上面的示例代码中整个 for 循环的循环体都被当做了临界区，多个线程线性地执行临界区代码，临界区越大程序效率越低，还是需要根据实际情况选择最优的解决方案。</p>
<h3 id="std-recursive-mutex"><a href="#std-recursive-mutex" class="headerlink" title="std::recursive_mutex"></a>std::recursive_mutex</h3><p>递归互斥锁 <code>std::recursive_mutex</code> 允许同一线程多次获得互斥锁，用来解决同一线程需要多次获取互斥量时死锁的问题，在下面的例子中使用独占非递归互斥量会发生死锁：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Calculate</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">Calculate</span>() : <span class="built_in">m_i</span>(<span class="number">6</span>) &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">mul</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;   </span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        m_i *= x;</span><br><span class="line">    &#125;   </span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">div</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        m_i /= x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">both</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="built_in">mul</span>(x);</span><br><span class="line">        <span class="built_in">div</span>(y);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> m_i;</span><br><span class="line">    mutex m_mutex;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Calculate cal;</span><br><span class="line">    cal.<span class="built_in">both</span>(<span class="number">6</span>, <span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ recursive_mutex.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>上面的程序中执行了 <code>cal.both(6, 3)</code> 调用之后，程序就会发生死锁。在 <code>both()</code> 中已经对互斥锁加锁，继续调用 <code>mul()</code> 函数，已经得到互斥锁所有权的线程再次获取这个互斥锁的所有权会造成死锁（C++ 中程序会异常退出，C 库函数会导致这个互斥锁永远无法被解锁，最终阻塞所有的线程）。</p>
<p>解决这个死锁，一个简单的办法就是使用递归互斥锁 <code>std::recursive_mutex</code>，它允许一个线程多次获得互斥锁的所有权。修改后的示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Calculate</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">Calculate</span>() : <span class="built_in">m_i</span>(<span class="number">6</span>) &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">mul</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;   </span><br><span class="line">        <span class="function">lock_guard&lt;recursive_mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        m_i *= x;</span><br><span class="line">    &#125;   </span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">div</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;recursive_mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        m_i /= x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">both</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;recursive_mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="built_in">mul</span>(x);</span><br><span class="line">        <span class="built_in">div</span>(y);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> m_i;</span><br><span class="line">    recursive_mutex m_mutex;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Calculate cal;</span><br><span class="line">    cal.<span class="built_in">both</span>(<span class="number">6</span>, <span class="number">3</span>);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;cal.m_i: &quot;</span> &lt;&lt; cal.m_i &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ recursive_mutex2.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>虽然递归互斥锁可以解决同一个互斥锁频繁获取互斥锁资源的问题，但是还是建议少用，主要原因如下：<br>（1）使用递归互斥锁的场景往往都是可以简化的，使用递归互斥锁很容易放纵复杂逻辑的产生，从而导致 bug 的产生。<br>（2）递归互斥锁比非递归互斥锁效率要低一些。<br>（3）递归互斥锁虽然允许同一个线程多次获得同一个互斥锁的所有权，但最大次数并未具体说明，一旦超过一定的次数，就会抛出 <code>std::system</code> 错误。</p>
<h3 id="std-timed-mutex"><a href="#std-timed-mutex" class="headerlink" title="std::timed_mutex"></a>std::timed_mutex</h3><p><code>std::timed_mutex</code> 是超时独占互斥锁，在获取互斥锁资源时增加了超时等待功能。因为不知道获取锁资源需要等待多长时间，为了保证不一直等待下去，设置了一个超时时长，超时后线程就可以解除阻塞去做其他事情。</p>
<p><code>std::timed_mutex</code> 比 <code>std::_mutex</code> 多了两个成员函数：<code>try_lock_for()</code> 和 <code>try_lock_until()</code></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">lock</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">try_lock</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">unlock</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// std::timed_mutex比std::_mutex多出的两个成员函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Rep</span>, <span class="keyword">class</span> <span class="title class_">Period</span>&gt;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">try_lock_for</span><span class="params">(<span class="type">const</span> chrono::duration&lt;Rep,Period&gt;&amp; rel_time)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Clock</span>, <span class="keyword">class</span> <span class="title class_">Duration</span>&gt;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">try_lock_until</span><span class="params">(<span class="type">const</span> chrono::time_point&lt;Clock,Duration&gt;&amp; abs_time)</span></span>;</span><br></pre></td></tr></table></figure>
<p><code>try_lock_for</code> 函数：当线程获取不到互斥锁资源的时候，让线程阻塞一定的时间长度。<br><code>try_lock_until</code> 函数：当线程获取不到互斥锁资源的时候，让线程阻塞到某一个指定的时间点。<br>关于两个函数的返回值：<br>（1）如果得到互斥锁的所有权之后，函数会马上解除阻塞，返回 true。<br>（2）如果阻塞的时长用完或者到达指定的时间点之后，函数也会解除阻塞，返回 false。</p>
<p><code>std::timed_mutex</code> 的使用，示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line">timed_mutex g_mutex;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">work</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">chrono::seconds <span class="title">timeout</span><span class="params">(<span class="number">1</span>)</span></span>;</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 通过阻塞一定的时长来争取互斥锁的所有权</span></span><br><span class="line">        <span class="keyword">if</span> (g_mutex.<span class="built_in">try_lock_for</span>(timeout))</span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;thread id: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;, get ownership&quot;</span> &lt;&lt; endl;</span><br><span class="line">            <span class="comment">// 模拟处理任务用了一定的时长</span></span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">10</span>));</span><br><span class="line">            <span class="comment">// 互斥锁解锁</span></span><br><span class="line">            g_mutex.<span class="built_in">unlock</span>();</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">else</span> </span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;thread id: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;, no get ownership&quot;</span> &lt;&lt; endl;</span><br><span class="line">            <span class="comment">// 模拟处理任务用了一定的时长</span></span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">milliseconds</span>(<span class="number">50</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(work)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(work)</span></span>;</span><br><span class="line"></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ timed_mutex.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>上面的示例代码中，通过一个 while 循环不停的去获取超时互斥锁的所有权，如果得不到就阻塞 1 秒钟，1 秒之后如果还是得不到阻塞 50 毫秒，然后再次继续尝试，直到获得互斥锁的所有权，跳出循环体。</p>
<p>关于递归超时互斥锁 <code>std::recursive_timed_mutex</code> 的使用方式和 <code>std::timed_mutex</code> 是一样的，只不过它可以允许一个线程多次获得互斥锁所有权，而 <code>timed_mutex</code> 只允许线程获取一次互斥锁所有权。另外，递归超时互斥锁 <code>std::recursive_timed_mutex</code> 也拥有 <code>std::recursive_mutex</code> 一样的弊端，不建议频繁使用。</p>
<h2 id="条件变量"><a href="#条件变量" class="headerlink" title="条件变量"></a>条件变量</h2><p>条件变量是 C++11 提供的另外一种用于等待的同步机制，它能阻塞一个或多个线程，直到收到另外一个线程发出的通知或者超时，才会唤醒当前阻塞的线程。条件变量需要和互斥量配合起来使用，C++11 提供了两种条件变量：<br>（1）condition_variable<br>需要配合 <code>std::unique_lock&lt;std::mutex&gt;</code> 进行 wait 操作，也就是阻塞线程的操作。<br>（2）condition_variable_any<br>和任意带有 <code>lock()</code>、<code>unlock()</code> 语义的 mutex 搭配使用，有四种：<br><code>std::mutex</code>：独占的非递归互斥锁<br><code>std::timed_mutex</code>：带超时的独占非递归互斥锁<br><code>std::recursive_mutex</code>：不带超时功能的递归互斥锁<br><code>std::recursive_timed_mutex</code>：带超时的递归互斥锁</p>
<p>条件变量通常用于生产者和消费者模型，使用过程如下：<br>（1）拥有条件变量的线程获取互斥量<br>（2）循环检查某个条件，如果条件不满足阻塞当前线程，否则线程继续向下执行<br>如果产品的数量达到上限，生产者阻塞；否则，生产者一直生产。<br>如果产品的数量为零，消费者阻塞；否则，消费者一直消费。<br>（3）条件满足之后，可以调用 <code>notify_one()</code> 或者 <code>notify_all()</code> 唤醒一个或者所有被阻塞的线程<br>消费者唤醒被阻塞的生产者，生产者解除阻塞继续生产。<br>生产者唤醒被阻塞的消费者，消费者解除阻塞继续消费。</p>
<h3 id="condition-variable"><a href="#condition-variable" class="headerlink" title="condition_variable"></a>condition_variable</h3><h4 id="成员函数-1"><a href="#成员函数-1" class="headerlink" title="成员函数"></a>成员函数</h4><p><code>condition_variable</code> 的成员函数主要分为两部分：线程等待（阻塞）函数和线程通知（唤醒）函数，这些函数被定义于头文件 <code>&lt;condition_variable&gt;</code>。<br>（1）等待函数<br>调用 <code>wait()</code> 函数的线程会被阻塞</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">wait</span><span class="params">(unique_lock&lt;mutex&gt;&amp; lck)</span></span>;</span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">class</span> Predicate&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">wait</span><span class="params">(unique_lock&lt;mutex&gt;&amp; lck, Predicate pred)</span></span>;</span><br></pre></td></tr></table></figure>
<p>函数1：调用该函数的线程直接被阻塞<br>函数2：该函数的第二个参数是一个判断条件，是一个返回值为布尔类型的函数。该参数可以传递一个有名函数的地址，也可以直接指定一个匿名函数。表达式返回 false 当前线程被阻塞；表达式返回 true 当前线程不会被阻塞，继续向下执行。</p>
<p>独占的互斥锁对象不能直接传递给 <code>wait()</code> 函数，需要通过模板类 <code>unique_lock</code> 进行二次处理，通过得到的对象仍然可以对独占的互斥锁对象做如下操作：<br><code>lock()</code>：锁定关联的互斥锁<br><code>try_lock()</code>：尝试锁定关联的互斥锁。若无法锁定，函数直接返回<br><code>try_lock_for()</code>：试图锁定关联的可定时锁定互斥锁。若互斥锁在给定时长中仍不能被锁定，函数返回<br><code>try_lock_until()</code>：试图锁定关联的可定时锁定互斥锁。若互斥锁在给定的时间点后仍不能被锁定，函数返回<br><code>unlock()</code>：将互斥锁解锁</p>
<p>如果线程被该函数阻塞，这个线程会释放占有的互斥锁的所有权，当阻塞解除之后这个线程会重新得到互斥锁的所有权，继续向下执行（这个过程是在函数内部完成的，了解这个过程即可，其目的是避免线程的死锁）。<br><code>wait_for()</code> 函数和 <code>wait()</code> 的功能是一样的，只不过多了一个阻塞时长。假设阻塞的线程没有被其他线程唤醒，当阻塞时长用完之后，线程就会自动解除阻塞，继续向下执行。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Rep</span>, <span class="keyword">class</span> <span class="title class_">Period</span>&gt;</span><br><span class="line"><span class="function">cv_status <span class="title">wait_for</span><span class="params">(unique_lock&lt;mutex&gt;&amp; lck,</span></span></span><br><span class="line"><span class="params"><span class="function">                   <span class="type">const</span> chrono::duration&lt;Rep,Period&gt;&amp; rel_time)</span></span>;</span><br><span class="line">	</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Rep</span>, <span class="keyword">class</span> <span class="title class_">Period</span>, <span class="keyword">class</span> <span class="title class_">Predicate</span>&gt;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">wait_for</span><span class="params">(unique_lock&lt;mutex&gt;&amp; lck,</span></span></span><br><span class="line"><span class="params"><span class="function">              <span class="type">const</span> chrono::duration&lt;Rep,Period&gt;&amp; rel_time, Predicate pred)</span></span>;</span><br></pre></td></tr></table></figure>

<p><code>wait_until()</code> 函数和 <code>wait_for()</code> 的功能是一样的，它是指定让线程阻塞到某一个时间点。假设阻塞的线程没有被其他线程唤醒，当到达指定的时间点之后，线程就会自动解除阻塞，继续向下执行。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Clock</span>, <span class="keyword">class</span> <span class="title class_">Duration</span>&gt;</span><br><span class="line"><span class="function">cv_status <span class="title">wait_until</span><span class="params">(unique_lock&lt;mutex&gt;&amp; lck,</span></span></span><br><span class="line"><span class="params"><span class="function">                     <span class="type">const</span> chrono::time_point&lt;Clock,Duration&gt;&amp; abs_time)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Clock</span>, <span class="keyword">class</span> <span class="title class_">Duration</span>, <span class="keyword">class</span> <span class="title class_">Predicate</span>&gt;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">wait_until</span><span class="params">(unique_lock&lt;mutex&gt;&amp; lck,</span></span></span><br><span class="line"><span class="params"><span class="function">                <span class="type">const</span> chrono::time_point&lt;Clock,Duration&gt;&amp; abs_time, Predicate pred)</span></span>;</span><br></pre></td></tr></table></figure>

<p>（2）通知函数</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">notify_one</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">notify_all</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>
<p><code>notify_one()</code>：唤醒一个被当前条件变量阻塞的线程<br><code>notify_all()</code>：唤醒全部被当前条件变量阻塞的线程</p>
<h4 id="生产者和消费者模型"><a href="#生产者和消费者模型" class="headerlink" title="生产者和消费者模型"></a>生产者和消费者模型</h4><p>使用条件变量来实现一个同步队列，这个队列作为生产者线程和消费者线程的共享资源，示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;condition_variable&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SyncQueue</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">SyncQueue</span>(<span class="type">int</span> maxSize) : <span class="built_in">m_maxSize</span>(maxSize) &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">put</span><span class="params">(<span class="type">const</span> <span class="type">int</span>&amp; x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">unique_lock&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="comment">// 判断任务队列是否已满</span></span><br><span class="line">        <span class="keyword">while</span> (m_queue.<span class="built_in">size</span>() == m_maxSize)</span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;task queue is full, wait...&quot;</span> &lt;&lt; endl;</span><br><span class="line">            <span class="comment">// 阻塞线程</span></span><br><span class="line">            m_notFull.<span class="built_in">wait</span>(locker);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 将任务放入任务队列中</span></span><br><span class="line">        m_queue.<span class="built_in">push_back</span>(x);</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; is produce&quot;</span> &lt;&lt; endl;</span><br><span class="line">        <span class="comment">// 通知消费者去消费</span></span><br><span class="line">        m_notEmpty.<span class="built_in">notify_one</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">take</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">unique_lock&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="keyword">while</span> (m_queue.<span class="built_in">empty</span>())</span><br><span class="line">        &#123;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot;task queue is empty, wait...&quot;</span> &lt;&lt; endl;</span><br><span class="line">            <span class="comment">// 阻塞线程</span></span><br><span class="line">            m_notEmpty.<span class="built_in">wait</span>(locker);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 从任务队列中取出任务</span></span><br><span class="line">        <span class="type">int</span> x = m_queue.<span class="built_in">front</span>();</span><br><span class="line">        m_queue.<span class="built_in">pop_front</span>();</span><br><span class="line">        <span class="comment">// 通知生产者去生产</span></span><br><span class="line">        m_notFull.<span class="built_in">notify_one</span>();</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; is consume&quot;</span> &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">empty</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="keyword">return</span> m_queue.<span class="built_in">empty</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">full</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="keyword">return</span> m_queue.<span class="built_in">size</span>() == m_maxSize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">size</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="keyword">return</span> m_queue.<span class="built_in">size</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    list&lt;<span class="type">int</span>&gt; m_queue;             <span class="comment">// 存储队列数据</span></span><br><span class="line">    <span class="type">int</span> m_maxSize;                 <span class="comment">// 任务队列的最大任务个数</span></span><br><span class="line">    mutex m_mutex;                 <span class="comment">// 互斥锁</span></span><br><span class="line">    condition_variable m_notEmpty; <span class="comment">// 不空的条件变量</span></span><br><span class="line">    condition_variable m_notFull;  <span class="comment">// 不满的条件变量</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">SyncQueue <span class="title">taskQ</span><span class="params">(<span class="number">50</span>)</span></span>;</span><br><span class="line">    <span class="keyword">auto</span> produce = <span class="built_in">bind</span>(&amp;SyncQueue::put, &amp;taskQ, placeholders::_1);</span><br><span class="line">    <span class="keyword">auto</span> consume = <span class="built_in">bind</span>(&amp;SyncQueue::take, &amp;taskQ);</span><br><span class="line">    thread t1[<span class="number">3</span>];</span><br><span class="line">    thread t2[<span class="number">3</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        t1[i] = <span class="built_in">thread</span>(produce, i + <span class="number">100</span>);</span><br><span class="line">        t2[i] = <span class="built_in">thread</span>(consume);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3</span>; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        t1[i].<span class="built_in">join</span>();</span><br><span class="line">        t2[i].<span class="built_in">join</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ producer_consumer.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>条件变量 condition_variable 类的 <code>wait()</code> 还有一个重载的方法，可以接受一个条件，这个条件也可以是一个返回值为布尔类型的函数，条件变量会先检查判断这个条件是否满足。<br>（1）如果满足条件，则当前线程重新获得互斥锁的所有权，结束阻塞，继续向下执行。<br>（2）如果不满足条件，则当前线程会释放互斥锁（解锁）同时被阻塞，等待被唤醒。</p>
<p>上面的示例代码中 <code>put()</code>、<code>take()</code> 修改如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">put</span><span class="params">(<span class="type">const</span> <span class="type">int</span>&amp; x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">unique_lock&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">    <span class="comment">// 根据条件阻塞线程</span></span><br><span class="line">    m_notFull.<span class="built_in">wait</span>(locker, [<span class="keyword">this</span>]() &#123;</span><br><span class="line">        <span class="keyword">return</span> m_queue.<span class="built_in">size</span>() != m_maxSize;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 将任务放入任务队列中</span></span><br><span class="line">    m_queue.<span class="built_in">push_back</span>(x);</span><br><span class="line">    cout &lt;&lt; x &lt;&lt; <span class="string">&quot; is produce&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 通知消费者去消费</span></span><br><span class="line">    m_notEmpty.<span class="built_in">notify_one</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">take</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">unique_lock&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">    m_notEmpty.<span class="built_in">wait</span>(locker, [<span class="keyword">this</span>]() &#123;</span><br><span class="line">        <span class="keyword">return</span> !m_queue.<span class="built_in">empty</span>();</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 从任务队列中取出任务</span></span><br><span class="line">    <span class="type">int</span> x = m_queue.<span class="built_in">front</span>();</span><br><span class="line">    m_queue.<span class="built_in">pop_front</span>();</span><br><span class="line">    <span class="comment">// 通知生产者去生产</span></span><br><span class="line">    m_notFull.<span class="built_in">notify_one</span>();</span><br><span class="line">    cout &lt;&lt; x &lt;&lt; <span class="string">&quot; is consume&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ producer_consumer2.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>修改之后程序变得更精简，而且执行效率更高，因为在这两个函数中的 while 循环被删掉了，但是最终的效果是一样的，推荐使用这种方式的 <code>wait()</code> 进行线程的阻塞。</p>
<h3 id="condition-variable-any"><a href="#condition-variable-any" class="headerlink" title="condition_variable_any"></a>condition_variable_any</h3><h4 id="成员函数-2"><a href="#成员函数-2" class="headerlink" title="成员函数"></a>成员函数</h4><p><code>condition_variable_any</code> 的成员函数也是分为两部分：线程等待（阻塞）函数 和线程通知（唤醒）函数，这些函数被定义于头文件 <code>&lt;condition_variable&gt;</code>。<br>（1）等待函数</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Lock</span>&gt; </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">wait</span><span class="params">(Lock&amp; lck)</span></span>;</span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Lock</span>, <span class="keyword">class</span> <span class="title class_">Predicate</span>&gt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">wait</span><span class="params">(Lock&amp; lck, Predicate pred)</span></span>;</span><br></pre></td></tr></table></figure>

<p><code>wait_for()</code> 函数和 <code>wait()</code> 的功能是一样的，只不过多了一个阻塞时长。假设阻塞的线程没有被其他线程唤醒，当阻塞时长用完之后，线程就会自动解除阻塞，继续向下执行。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Lock</span>, <span class="keyword">class</span> <span class="title class_">Rep</span>, <span class="keyword">class</span> <span class="title class_">Period</span>&gt;</span><br><span class="line"><span class="function">cv_status <span class="title">wait_for</span><span class="params">(Lock&amp; lck, <span class="type">const</span> chrono::duration&lt;Rep,Period&gt;&amp; rel_time)</span></span>;</span><br><span class="line">	</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Lock</span>, <span class="keyword">class</span> <span class="title class_">Rep</span>, <span class="keyword">class</span> <span class="title class_">Period</span>, <span class="keyword">class</span> <span class="title class_">Predicate</span>&gt;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">wait_for</span><span class="params">(Lock&amp; lck, <span class="type">const</span> chrono::duration&lt;Rep,Period&gt;&amp; rel_time, Predicate pred)</span></span>;</span><br></pre></td></tr></table></figure>

<p><code>wait_until()</code> 函数和 <code>wait_for()</code> 的功能是一样的，它是指定让线程阻塞到某一个时间点，假设阻塞的线程没有被其他线程唤醒，当到达指定的时间点之后，线程就会自动解除阻塞，继续向下执行。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Lock</span>, <span class="keyword">class</span> <span class="title class_">Clock</span>, <span class="keyword">class</span> <span class="title class_">Duration</span>&gt;</span><br><span class="line"><span class="function">cv_status <span class="title">wait_until</span><span class="params">(Lock&amp; lck, <span class="type">const</span> chrono::time_point&lt;Clock,Duration&gt;&amp; abs_time)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Lock</span>, <span class="keyword">class</span> <span class="title class_">Clock</span>, <span class="keyword">class</span> <span class="title class_">Duration</span>, <span class="keyword">class</span> <span class="title class_">Predicate</span>&gt;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">wait_until</span><span class="params">(Lock&amp; lck, <span class="type">const</span> chrono::time_point&lt;Clock,Duration&gt;&amp; abs_time, Predicate pred)</span></span>;</span><br></pre></td></tr></table></figure>

<p>（2）通知函数</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">notify_one</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">notify_all</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>
<p><code>notify_one()</code>：唤醒一个被当前条件变量阻塞的线程<br><code>notify_all()</code>：唤醒全部被当前条件变量阻塞的线程</p>
<h4 id="生产者和消费者模型-1"><a href="#生产者和消费者模型-1" class="headerlink" title="生产者和消费者模型"></a>生产者和消费者模型</h4><p>使用条件变量 <code>condition_variable_any</code> 同样可以实现上面的生产者和消费者的例子，代码只有个别细节不同：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;condition_variable&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SyncQueue</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">SyncQueue</span>(<span class="type">int</span> maxSize) : <span class="built_in">m_maxSize</span>(maxSize) &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">put</span><span class="params">(<span class="type">const</span> <span class="type">int</span>&amp; x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="comment">// 根据条件阻塞线程</span></span><br><span class="line">        m_notFull.<span class="built_in">wait</span>(m_mutex, [<span class="keyword">this</span>]() &#123;</span><br><span class="line">            <span class="keyword">return</span> m_queue.<span class="built_in">size</span>() != m_maxSize;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">// 将任务放入任务队列中</span></span><br><span class="line">        m_queue.<span class="built_in">push_back</span>(x);</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; is produce&quot;</span> &lt;&lt; endl;</span><br><span class="line">        <span class="comment">// 通知消费者去消费</span></span><br><span class="line">        m_notEmpty.<span class="built_in">notify_one</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">take</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        m_notEmpty.<span class="built_in">wait</span>(m_mutex, [<span class="keyword">this</span>]() &#123;</span><br><span class="line">            <span class="keyword">return</span> !m_queue.<span class="built_in">empty</span>();</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="comment">// 从任务队列中取出任务</span></span><br><span class="line">        <span class="type">int</span> x = m_queue.<span class="built_in">front</span>();</span><br><span class="line">        m_queue.<span class="built_in">pop_front</span>();</span><br><span class="line">        <span class="comment">// 通知生产者去生产</span></span><br><span class="line">        m_notFull.<span class="built_in">notify_one</span>();</span><br><span class="line">        cout &lt;&lt; x &lt;&lt; <span class="string">&quot; is consume&quot;</span> &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">empty</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="keyword">return</span> m_queue.<span class="built_in">empty</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">full</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="keyword">return</span> m_queue.<span class="built_in">size</span>() == m_maxSize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">size</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="function">lock_guard&lt;mutex&gt; <span class="title">locker</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line">        <span class="keyword">return</span> m_queue.<span class="built_in">size</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    list&lt;<span class="type">int</span>&gt; m_queue;                  <span class="comment">// 存储队列数据</span></span><br><span class="line">    <span class="type">int</span> m_maxSize;                      <span class="comment">// 任务队列的最大任务个数</span></span><br><span class="line">    mutex m_mutex;                      <span class="comment">// 互斥锁</span></span><br><span class="line">    condition_variable_any  m_notEmpty; <span class="comment">// 不空的条件变量</span></span><br><span class="line">    condition_variable_any  m_notFull;  <span class="comment">// 不满的条件变量</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">SyncQueue <span class="title">taskQ</span><span class="params">(<span class="number">50</span>)</span></span>;</span><br><span class="line">    <span class="keyword">auto</span> produce = <span class="built_in">bind</span>(&amp;SyncQueue::put, &amp;taskQ, placeholders::_1);</span><br><span class="line">    <span class="keyword">auto</span> consume = <span class="built_in">bind</span>(&amp;SyncQueue::take, &amp;taskQ);</span><br><span class="line">    thread t1[<span class="number">3</span>];</span><br><span class="line">    thread t2[<span class="number">3</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        t1[i] = <span class="built_in">thread</span>(produce, i + <span class="number">100</span>);</span><br><span class="line">        t2[i] = <span class="built_in">thread</span>(consume);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3</span>; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        t1[i].<span class="built_in">join</span>();</span><br><span class="line">        t2[i].<span class="built_in">join</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ producer_consumer3.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>以上介绍的两种互斥锁各自有各自的特点，<code>condition_variable</code> 配合 <code>unique_lock</code> 使用更灵活一些，可以在任何时候自由地释放互斥锁，而 <code>condition_variable_any</code> 如果和 <code>lock_guard</code> 一起使用必须要等到其生命周期结束才能将互斥锁释放。<br>但是，<code>condition_variable_any</code> 可以和多种互斥锁配合使用，应用场景也更广，而 <code>condition_variable</code> 只能和独占的非递归互斥锁<code>mutex</code>配合使用，有一定的局限性。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a target="_blank" rel="noopener" href="https://subingwen.cn/cpp/mutex">https://subingwen.cn/cpp/mutex</a><br><a target="_blank" rel="noopener" href="https://subingwen.cn/cpp/condition">https://subingwen.cn/cpp/condition</a></p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">线程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/27/Cpp%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-命名空间this-thread" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/25/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4this-thread/">命名空间this_thread</a>
    </h1>
  

        
        
<a href="/2021/08/25/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4this-thread/" class="archive-article-date">
  	<time datetime="2021-08-25T11:07:21.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-25</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>在 C++11 中不仅添加了线程类，还添加了一个关于线程的命名空间 <code>std::this_thread</code>，在这个命名空间中提供了四个公共的成员函数，通过这些成员函数就可以对当前线程进行相关的操作。</p>
<h2 id="get-id"><a href="#get-id" class="headerlink" title="get_id()"></a>get_id()</h2><p>调用命名空间 <code>std::this_thread</code> 中的 <code>get_id()</code> 方法可以得到当前线程的线程 ID，函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">thread::id <span class="title">get_id</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;child thread: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;main thread: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ get_id3.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>程序启动，开始执行 <code>main()</code> 函数，此时只有一个线程也就是主线程。当创建了子线程对象 t 之后，指定的函数 <code>func()</code> 会在子线程中执行，这时通过调用 <code>this_thread::get_id()</code> 获得当前线程的线程 ID。</p>
<h2 id="sleep-for"><a href="#sleep-for" class="headerlink" title="sleep_for()"></a>sleep_for()</h2><p>线程被创建后也有这五种状态：创建态、就绪态、运行态，阻塞态（挂起态）、退出态（终止态）。关于状态之间的转换是一样的，参考进程。<br>线程和进程的执行有很多相似之处，在计算机中启动的多个线程都需要占用 CPU 资源，但是 CPU 的个数是有限的并且每个 CPU 在同一时间点不能同时处理多个任务。</p>
<p>为了能够实现并发处理，多个线程都是分时复用 CPU 时间片，快速的交替处理各个线程中的任务。因此多个线程之间需要争抢 CPU 时间片，抢到了就执行，抢不到则无法执行（因为默认所有的线程优先级都相同，内核也会从中调度，不会出现某个线程永远抢不到 CPU 时间片的情况）。</p>
<p>命名空间 <code>this_thread</code> 中提供了一个休眠函数 <code>sleep_for()</code>，调用这个函数的线程会马上从运行态变成阻塞态并在这种状态下休眠一定的时长，因为阻塞态的线程已经让出了 CPU 资源，代码也不会被执行，所以线程休眠过程中对 CPU 来说没有任何负担。函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Rep</span>, <span class="keyword">class</span> <span class="title class_">Period</span>&gt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sleep_for</span> <span class="params">(<span class="type">const</span> chrono::duration&lt;Rep,Period&gt;&amp; rel_time)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    参数：指定一个休眠时长，是一个时间段</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<p>示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;child thread: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;, i: &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;main thread: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ sleep_for.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>在 <code>func()</code> 函数的 for 循环中使用了 <code>this_thread::sleep_for(chrono::seconds(1))</code> 之后，每循环一次程序都会阻塞 1 秒钟，也就是说每隔 1 秒才会进行一次输出。注意：程序休眠完成之后，会从阻塞态重新变成就绪态，就绪态的线程需要再次争抢 CPU 时间片，抢到之后才会变成运行态，这时候程序才会继续向下运行。</p>
<h2 id="sleep-until"><a href="#sleep-until" class="headerlink" title="sleep_until()"></a>sleep_until()</h2><p>命名空间 <code>this_thread</code> 中提供了另一个休眠函数 <code>sleep_until()</code> 和 <code>sleep_for()</code> 不同在于参数类型不一样。<br><code>sleep_until()</code>：指定线程阻塞到某一个指定的时间点 time_point 类型，之后解除阻塞。<br><code>sleep_for()</code>：指定线程阻塞一定的时间长度 duration 类型，之后解除阻塞。</p>
<p>函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">Clock</span>, <span class="keyword">class</span> <span class="title class_">Duration</span>&gt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sleep_until</span><span class="params">(<span class="type">const</span> chrono::time_point&lt;Clock,Duration&gt;&amp; abs_time)</span></span>;</span><br></pre></td></tr></table></figure>

<p>示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 获取当前的系统时间点</span></span><br><span class="line">        <span class="keyword">auto</span> now = chrono::system_clock::<span class="built_in">now</span>();</span><br><span class="line">        <span class="comment">// 时间间隔为 2s</span></span><br><span class="line">        <span class="function">chrono::seconds <span class="title">sec</span><span class="params">(<span class="number">2</span>)</span></span>;</span><br><span class="line">        <span class="comment">// 当前时间点之后休眠两秒</span></span><br><span class="line">        this_thread::<span class="built_in">sleep_until</span>(now + sec);</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;child thread: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;, i: &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;main thread: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="function">thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ sleep_until.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p><code>sleep_until()</code> 和 <code>sleep_for()</code> 函数的功能是一样的，只不过前者是基于时间点去阻塞线程，后者是基于时间段去阻塞线程。<br>项目开发过程中，根据实际情况选择最优的解决方案。</p>
<h2 id="yield"><a href="#yield" class="headerlink" title="yield()"></a>yield()</h2><p>命名空间 <code>this_thread</code> 中提供了一个非常绅士的函数 <code>yield()</code>，在线程中调用这个函数之后，处于运行态的线程会<strong>主动让出</strong>自己已经抢到的 CPU 时间片，最终变为就绪态，这样其它的线程就有更大的概率能够抢到 CPU 时间片了。<br>注意：线程调用了 <code>yield ()</code> 之后会主动放弃 CPU 资源，但是这个变为就绪态的线程会马上参与到下一轮 CPU 的抢夺，不排除它能继续抢到 CPU 时间片的情况，这是概率问题。</p>
<p>函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">yield</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;child thread: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;, i: &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">        this_thread::<span class="built_in">yield</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(func)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(func)</span></span>;</span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ yield.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>在上面的程序中，执行 <code>func()</code> 中的 for 循环会占用大量的时间，在极端情况下，如果当前线程占用 CPU 资源不释放就会导致其他线程中的任务无法被处理，或者该线程每次都能抢到 CPU 时间片，导致其他线程中的任务没有机会被执行。解决方案就是每执行一次循环，让该线程主动放弃 CPU 资源，重新和其他线程再次抢夺 CPU 时间片，如果其他线程抢到了 CPU 时间片就可以执行相应的任务。</p>
<p><code>std::this_thread::yield()</code>目的是避免一个线程长时间占用CPU资源，从而导致多线程处理性能下降。让当前线程主动放弃了当前自己抢到的CPU资源，但是在下一轮还会继续抢。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a target="_blank" rel="noopener" href="https://subingwen.cn/cpp/this_thread">https://subingwen.cn/cpp/this_thread</a></p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">线程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/25/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4this-thread/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-Cpp线程" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/24/Cpp%E7%BA%BF%E7%A8%8B/">C++线程</a>
    </h1>
  

        
        
<a href="/2021/08/24/Cpp%E7%BA%BF%E7%A8%8B/" class="archive-article-date">
  	<time datetime="2021-08-24T00:26:15.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-24</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h1 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h1><p>C++11 之前，C++ 语言没有对并发编程提供语言级别的支持，在编写可移植的并发程序时存在诸多的不便。<br>现在 C++11 中增加了线程以及线程相关的类，很方便地支持了并发编程，使得编写的多线程程序的可移植性得到了很大的提高。</p>
<p>C++11 中提供的线程类叫做 std::thread，基于这个类创建一个新的线程非常的简单，<br>只需要提供线程函数或者函数对象，并且可以同时指定线程函数的参数。<br>首先了解一下这个类提供的一些常用 API：</p>
<h2 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="built_in">thread</span>() <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="built_in">thread</span>(thread&amp;&amp; other) <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="comment">// 3</span></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">class</span> Function, <span class="keyword">class</span>... Args &gt;</span></span><br><span class="line"><span class="function"><span class="keyword">explicit</span> <span class="title">thread</span><span class="params">(Function&amp;&amp; f, Args&amp;&amp;... args)</span></span>;</span><br><span class="line"><span class="comment">// 4</span></span><br><span class="line"><span class="built_in">thread</span>(<span class="type">const</span> thread&amp;) = <span class="keyword">delete</span>;</span><br></pre></td></tr></table></figure>
<p>（1）构造函数1<br>默认构造函数。构造一个线程对象，在这个线程中不执行任何处理动作。<br>（2）构造函数2<br>移动构造函数。将 other 的线程所有权转移给新的 thread 对象。之后 other 不再表示执行线程。<br>（3）构造函数3<br>创建线程对象，并在该线程中执行函数 f 中的业务逻辑，args 是要传递给函数 f 的参数<br>任务函数 f 的可选类型有很多，具体如下：<br>    普通函数、类成员函数、匿名函数、仿函数（这些都是可调用对象类型）<br>    可以是可调用对象包装器类型，也可以是使用绑定器绑定之后得到的类型（仿函数）<br>（4）构造函数4<br>使用 =delete 删除拷贝构造，不允许线程对象之间的拷贝。</p>
<h2 id="公共成员函数"><a href="#公共成员函数" class="headerlink" title="公共成员函数"></a>公共成员函数</h2><h3 id="get-id"><a href="#get-id" class="headerlink" title="get_id()"></a>get_id()</h3><p>应用程序启动之后默认只有一个线程，这个线程一般称之为主线程或父线程，<br>通过线程类创建出的线程一般称之为子线程，每个被创建出的线程实例都对应一个线程 ID，这个 ID 是唯一的，<br>可以通过这个 ID 来区分和识别各个已经存在的线程实例，这个获取线程 ID 的函数 <code>get_id()</code>，函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">std::<span class="function">thread::id <span class="title">get_id</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func1</span><span class="params">(<span class="type">int</span> num, string str)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;child thread, i: &quot;</span> &lt;&lt; i &lt;&lt; <span class="string">&quot;, num: &quot;</span></span><br><span class="line">             &lt;&lt; num &lt;&lt; <span class="string">&quot;, str: &quot;</span> &lt;&lt; str &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func2</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;child thread, i: &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;main thread, id: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 创建了子线程对象 t，func() 函数会在这个子线程中运行</span></span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(func1, <span class="number">520</span>, <span class="string">&quot;hello world&quot;</span>)</span></span>;</span><br><span class="line">    <span class="comment">// 创建了子线程对象 t1，任务函数 func1()</span></span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(func2)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;thread t, id: &quot;</span> &lt;&lt; t1.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;thread t1, id: &quot;</span> &lt;&lt; t2.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ get_id.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>通过线程对象调用 <code>get_id()</code> 获取这个子线程的线程 ID。<br>示例代码中有一个 bug，在主线程中依次创建出两个子线程，打印两个子线程的线程 ID，最后主线程执行完毕就退出（主线程执行 main 函数）。默认情况下，主线程销毁时会将与其关联的两个子线程也一起销毁，但是可能子线程中的任务还没有执行完毕，最后得不到想要的结果。</p>
<p>当创建了一个 thread 对象之后，在这个线程结束的时候，如何回收线程所使用的资源呢？thread 库提供了两种选择：<br>（1）加入式，<code>join()</code><br>（2）分离式，<code>detach()</code><br>另外，必须在线程对象销毁之前在二者之间作出选择，否则程序运行期间就会有 bug 产生。</p>
<h3 id="join"><a href="#join" class="headerlink" title="join()"></a>join()</h3><p><code>join()</code> 是连接一个线程，意味着主动地等待线程的终止（线程阻塞）。在某个线程中通过子线程对象调用 <code>join()</code> 函数，调用这个函数的线程被阻塞，但是子线程对象中的任务函数会继续执行，当任务执行完毕之后 <code>join()</code> 会清理当前子线程中的相关资源然后返回，调用该函数的线程解除阻塞继续向下执行。</p>
<p>注意：弄清楚这个函数阻塞的是哪一个线程，函数在哪个线程中被执行，函数就阻塞哪个线程。函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">join</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>

<p>使用线程阻塞函数之后，就可以解决在上面的示例代码中的 bug。如果要阻塞主线程的执行，只需要在主线程中通过子线程对象调用这个方法，当调用这个方法的子线程对象中的任务函数执行完毕之后，主线程的阻塞也就随之解除。修改后的示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;main thread, id: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(func1, <span class="number">520</span>, <span class="string">&quot;hello world&quot;</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(func2)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;thread t, id: &quot;</span> &lt;&lt; t1.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;thread t1, id: &quot;</span> &lt;&lt; t2.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当主线程运行到 <code>t.join()</code>，根据子线程对象 t 的任务函数 <code>func()</code> 的执行情况，主线程会做如下处理：<br>（1）如果任务函数 <code>func()</code> 还没执行完毕：主线程阻塞，直到任务执行完毕，主线程解除阻塞，继续向下运行。<br>（2）如果任务函数<code> func()</code> 已经执行完毕：主线程不会阻塞，继续向下运行。</p>
<p>为了更好地理解 <code>join()</code> 的使用，举一个例子，场景如下：<br>程序中一共有三个线程，其中两个子线程负责分段下载同一个文件。下载完毕之后，由主线程对这个文件进行下一步处理，示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">download1</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 模拟下载，总共耗时 500ms，阻塞线程 500ms</span></span><br><span class="line">    this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">microseconds</span>(<span class="number">500</span>));</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;thread t1, id: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">download2</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 模拟下载，总共耗时 300ms，阻塞线程 300ms</span></span><br><span class="line">    this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">microseconds</span>(<span class="number">300</span>));</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;thread t2, id: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">doSomething</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;start...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;...&quot;</span> &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;end...&quot;</span> &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(download1)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(download2)</span></span>;</span><br><span class="line">    <span class="comment">// 阻塞主线程，等待所有子线程任务执行完毕再继续向下执行</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    <span class="built_in">doSomething</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ join.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>示例代码中最核心的处理是在主线程调用 <code>doSomething()</code> 。<br>之前通过子线程对象调用了 <code>join()</code>  方法，这样能够保证两个子线程的任务都执行完毕（此处表示文件内容已经全部下载完成），主线程再对文件进行后续处理。如果子线程的文件没有下载完毕，主线程就去处理文件，从逻辑上是有问题的。</p>
<h3 id="detach"><a href="#detach" class="headerlink" title="detach()"></a>detach()</h3><p><code>detach()</code> 函数的作用是进行线程分离，分离主线程和创建出的子线程。<br>在线程分离之后，主线程退出也会一并销毁创建出的所有子线程。在主线程退出之前，子线程可以脱离主线程继续独立的运行，任务执行完毕之后，这个子线程会自动释放自己占用的系统资源。函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">detach</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>

<p>线程分离函数没有参数也没有返回值，只需要在线程成功之后，通过线程对象调用该函数，修改示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func1</span><span class="params">(<span class="type">int</span> num, string str)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;child thread, i: &quot;</span> &lt;&lt; i &lt;&lt; <span class="string">&quot;, num: &quot;</span></span><br><span class="line">             &lt;&lt; num &lt;&lt; <span class="string">&quot;, str: &quot;</span> &lt;&lt; str &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func2</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;child thread, i: &quot;</span> &lt;&lt; i &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;main thread, id: &quot;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    </span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(func1, <span class="number">520</span>, <span class="string">&quot;hello world&quot;</span>)</span></span>;</span><br><span class="line">    <span class="function">thread <span class="title">t2</span><span class="params">(func2)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;thread t, id: &quot;</span> &lt;&lt; t1.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;thread t1, id: &quot;</span> &lt;&lt; t2.<span class="built_in">get_id</span>() &lt;&lt; endl;</span><br><span class="line">    t1.<span class="built_in">detach</span>();</span><br><span class="line">    t2.<span class="built_in">detach</span>();</span><br><span class="line">    <span class="comment">// 让主线程休眠，等待子线程执行完毕</span></span><br><span class="line">    this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">5</span>));</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ detach.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>注意：线程分离函数 <code>detach()</code> 不会阻塞线程，子线程和主线程分离之后，在主线程中就不能再对这个子线程做任何控制，建议使用 <code>join()</code>。</p>
<h3 id="joinable"><a href="#joinable" class="headerlink" title="joinable()"></a>joinable()</h3><p><code>joinable()</code> 函数用于判断主线程和子线程是否关联（连接）状态，该函数返回一个布尔类型：<br>（1）返回值为 true：主线程和子线程之间有关联（连接）关系<br>（2）返回值为 false：主线程和子线程之间没有关联（连接）关系</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">joinable</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    thread t;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;before starting, joinable: &quot;</span> &lt;&lt; t.<span class="built_in">joinable</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    t = <span class="built_in">thread</span>(foo);</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;after starting, joinable: &quot;</span> &lt;&lt; t.<span class="built_in">joinable</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;after joining, joinable: &quot;</span> &lt;&lt; t.<span class="built_in">joinable</span>() &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="function">thread <span class="title">t1</span><span class="params">(foo)</span></span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;after starting, joinable: &quot;</span> &lt;&lt; t1.<span class="built_in">joinable</span>() &lt;&lt; endl;</span><br><span class="line">    t1.<span class="built_in">detach</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;after starting, joinable: &quot;</span> &lt;&lt; t1.<span class="built_in">joinable</span>() &lt;&lt; endl;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ joinable.cpp -o app</span></span><br></pre></td></tr></table></figure>
<p>在创建的子线程对象的时候，如果没有指定任务函数，那么子线程不会启动，主线程和这个子线程也不会进行连接。<br>在创建的子线程对象的时候，如果指定了任务函数，子线程启动并执行任务，主线程和这个子线程自动连接成功。<br>在子线程调用了 <code>join()</code> 函数，子线程中的任务函数继续执行，直到任务处理完毕，<code>join()</code> 会回收当前子线程的相关资源，所以这个子线程和主线程的连接断开。因此，调用 <code>join()</code> 之后再调用 <code>joinable()</code> 会返回 false。<br>子线程调用了 <code>detach()</code> 函数之后，父子线程分离，同时二者的连接断开，调用 <code>joinable()</code> 返回 false。</p>
<h3 id="operator"><a href="#operator" class="headerlink" title="operator=()"></a>operator=()</h3><p>线程中的资源是不能被复制的，因此通过 = 操作符进行赋值操作最终并不会得到两个完全相同的对象。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// move</span></span><br><span class="line">thread&amp; <span class="keyword">operator</span>= (thread&amp;&amp; other) <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="comment">// copy [deleted]	</span></span><br><span class="line">thread&amp; <span class="keyword">operator</span>= (<span class="type">const</span> other&amp;) = <span class="keyword">delete</span>;</span><br></pre></td></tr></table></figure>
<p>通过以上 = 操作符的重载声明可知：<br>（1）如果 other 是一个右值，会进行资源所有权的转移。<br>（2）如果 other 不是右值，禁止拷贝，该函数被删除（=delete），不可用。</p>
<h2 id="静态函数"><a href="#静态函数" class="headerlink" title="静态函数"></a>静态函数</h2><p>thread 线程类还提供了一个静态方法，用于获取当前计算机的 CPU 核心数，根据这个结果在程序中创建出数量相等的线程。每个线程独自占有一个 CPU 核心，这些线程就不用分时复用 CPU 时间片，此时程序的并发效率是最高的。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> <span class="type">unsigned</span> <span class="title">hardware_concurrency</span><span class="params">()</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure>

<p>示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> num = thread::<span class="built_in">hardware_concurrency</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;CPU number: &quot;</span> &lt;&lt; num &lt;&lt; endl;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// g++ hardware_concurrency.cpp -o app</span></span><br></pre></td></tr></table></figure>

<h2 id="C线程库"><a href="#C线程库" class="headerlink" title="C线程库"></a>C线程库</h2><p>C 语言提供的线程库不论在 window 还是 Linux 操作系统中都可以使用，C 语言中的线程函数和上面的 C++ 线程类使用类似（基于面向对象的思想进行了封装），但 C++ 的线程类用起来更简单一些。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a target="_blank" rel="noopener" href="https://subingwen.cn/cpp/thread/">https://subingwen.cn/cpp/thread/</a><br><a target="_blank" rel="noopener" href="https://lcf163.github.io/2021/08/10/%E7%BA%BF%E7%A8%8B/">https://lcf163.github.io/2021/08/10/线程/</a></p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">线程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/24/Cpp%E7%BA%BF%E7%A8%8B/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-线程同步" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/20/%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5/">线程同步</a>
    </h1>
  

        
        
<a href="/2021/08/20/%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5/" class="archive-article-date">
  	<time datetime="2021-08-20T05:54:38.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-20</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="线程同步的概念"><a href="#线程同步的概念" class="headerlink" title="线程同步的概念"></a>线程同步的概念</h2><p>假设有 4 个线程 A、B、C、D，当前一个线程 A 对内存中的共享资源进行访问的时候，其他线程 B、C、D 都不可以对这块内存进行操作，直到线程 A 对这块内存访问完毕为止，B、C、D 中的一个才能访问这块内存，剩余的两个需要继续阻塞等待，以此类推，直至所有的线程都对这块内存操作完毕。<br>线程对内存的这种访问方式就称之为<strong>线程同步</strong>，所谓的同步并不是多个线程同时对内存进行访问，而是按照先后顺序依次进行的。</p>
<h3 id="为什么要同步"><a href="#为什么要同步" class="headerlink" title="为什么要同步"></a>为什么要同步</h3><p>例子：两个线程交替数数（每个线程数 50 个数，交替数到 100）</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX 50</span></span><br><span class="line"><span class="comment">// 全局变量</span></span><br><span class="line"><span class="type">int</span> number;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 线程处理函数</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">funcA</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++) </span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">int</span> cur = number;</span><br><span class="line">        cur ++;</span><br><span class="line">        <span class="built_in">usleep</span>(<span class="number">10</span>);</span><br><span class="line">        number = cur;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Thread id = %lu, number = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), number);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">funcB</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++) </span><br><span class="line">    &#123;</span><br><span class="line">        <span class="type">int</span> cur = number;</span><br><span class="line">        cur ++;</span><br><span class="line">        number = cur;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Thread id = %lu, number = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), number);</span><br><span class="line">        <span class="built_in">usleep</span>(<span class="number">5</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">const</span> <span class="type">char</span>* argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">pthread_t</span> p1, p2;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程</span></span><br><span class="line">    <span class="built_in">pthread_create</span>(&amp;p1, <span class="literal">NULL</span>, funcA, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">pthread_create</span>(&amp;p2, <span class="literal">NULL</span>, funcB, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞，回收资源</span></span><br><span class="line">    <span class="built_in">pthread_join</span>(p1, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">pthread_join</span>(p2, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// gcc .\counter.c -lpthread -o app</span></span><br></pre></td></tr></table></figure>
<p>上面的例子进行测试，虽然每个线程内部循环了 50 次每次数一数，但是最终没有数到 100。<br>通过输出的结果可知，有些数字被重复数了多次，原因就是没有对线程进行同步处理，造成了数据的混乱。</p>
<p>两个线程在数数的时候需要分时复用 CPU 时间片，并且测试程序中调用了 <code>sleep()</code> 导致线程的 CPU 时间片没用完就被迫挂起，这样就能让 CPU 的上下文切换（保存当前状态，下一次继续运行的时候需要加载保存的状态）更加频繁，更容易再现数据混乱的这个现象。CPU 对应寄存器、一级缓存、二级缓存、三级缓存是独占的，用于存储处理的数据和线程的状态信息，数据被 CPU 处理完成需要再次被写入到物理内存中，物理内存数据也可以通过文件 IO 操作写入到磁盘中。</p>
<p>在测试程序中两个线程共用全局变量 <code>number</code>。当线程变成运行态之后开始数数，从物理内存加载数据，然后将数据放到 CPU 进行运算，最后将结果更新到物理内存中。如果数数的两个线程都可以顺利完成这个流程，那么得到的结果肯定是正确的。如果线程 A 执行这个过程期间就失去了 CPU 时间片，线程 A 被挂起了最新的数据没能更新到物理内存。线程 B 变成运行态之后从物理内存读数据，很显然它没有拿到最新数据，只能基于旧的数据往后数，然后失去 CPU 时间片挂起。线程 A 得到 CPU 时间片变成运行态，将上次没更新到内存的数据更新到内存，但是这样会导致线程 B 已经更新到内存的数据被覆盖，最终导致有些数据被重复数多次。</p>
<h3 id="同步方式"><a href="#同步方式" class="headerlink" title="同步方式"></a>同步方式</h3><p>对于多个线程访问共享资源出现数据混乱的问题，需要进行线程同步。常用的线程同步方式有四种：<br>（1）互斥锁<br>（2）读写锁<br>（3）条件变量<br>（4）信号量<br>所谓的共享资源就是多个线程共同访问的变量，这些变量通常为全局数据区变量或者堆区变量，这些变量对应的共享资源也被称为临界资源。</p>
<p>找到临界资源之后，再找和临界资源相关的上下文代码，这样就得到了一个代码块，这个代码块可以称之为临界区。<br>确定好临界区（临界区越小越好）之后，就可以进行线程同步，线程同步的大致处理思路：<br>（1）在临界区代码之上，添加加锁函数，对临界区加锁。<br>哪个线程调用这句代码，就会把这把锁锁上，其他线程就只能阻塞在锁上。<br>（2）在临界区代码之下，添加解锁函数，对临界区解锁。<br>出临界区的线程会将锁定的那把锁打开，其他抢到锁的线程就可以进入到临界区。<br>（3）锁机制能保证临界区代码最多只能同时有一个线程访问，这样并行访问就变为串行访问。</p>
<h2 id="互斥锁"><a href="#互斥锁" class="headerlink" title="互斥锁"></a>互斥锁</h2><h3 id="互斥锁函数"><a href="#互斥锁函数" class="headerlink" title="互斥锁函数"></a>互斥锁函数</h3><p>互斥锁是线程同步最常用的一种方式，通过互斥锁可以锁定一个代码块。<br>被锁定的这个代码块，所有的线程只能顺序执行，这样多线程访问共享资源数据混乱的问题就可以被解决。<br>付出的代价就是执行效率的降低，因为默认临界区多个线程是可以并行处理的，现在只能串行处理。</p>
<p>在 Linux 中互斥锁的类型为 <code>pthread_mutex_t</code>，创建一个这种类型的变量就得到了一把互斥锁。<br>在创建的锁对象中保存了当前这把锁的状态信息：锁定还是打开，如果是锁定状态还记录了给这把锁加锁的线程信息（线程 ID）。<br>一个互斥锁变量只能被一个线程锁定，被锁定之后其他线程再对互斥锁变量加锁就会被阻塞，直到这把互斥锁被解锁，被阻塞的线程才能被解除阻塞。<br>一般情况下，每一个共享资源对应一个把互斥锁，锁的个数和线程的个数无关。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 初始化互斥锁</span></span><br><span class="line"><span class="comment">// restrict: 是一个关键字, 用来修饰指针, 只有这个关键字修饰的指针可以访问指向的内存地址, 其他指针是不行的</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_mutex_init</span><span class="params">(<span class="type">pthread_mutex_t</span> *restrict mutex,</span></span></span><br><span class="line"><span class="params"><span class="function">           <span class="type">const</span> <span class="type">pthread_mutexattr_t</span> *restrict attr)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数:</span></span><br><span class="line"><span class="comment">    mutex: </span></span><br><span class="line"><span class="comment">        互斥锁变量的地址</span></span><br><span class="line"><span class="comment">    attr: </span></span><br><span class="line"><span class="comment">        互斥锁的属性，一般使用默认属性，这个参数指定为 NULL</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 释放互斥锁资源            </span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_mutex_destroy</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex)</span></span>;</span><br></pre></td></tr></table></figure>

<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 修改互斥锁的状态, 将其设定为锁定状态, 这个状态被写入到参数 mutex</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_mutex_lock</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex)</span></span>;</span><br></pre></td></tr></table></figure>
<p>这个函数被调用，首先会判断参数 <code>mutex</code> 互斥锁中的状态是否为锁定状态：<br>（1）如果没有被锁定，那么这个线程可以加锁成功，这个锁中会记录是哪个线程加锁成功；<br>（2）如果被锁定，其他线程加锁就失败，这些线程都会阻塞在这把锁上。<br>当这把锁被解开之后，这些阻塞在锁上的线程就解除阻塞，并且这些线程是通过竞争的方式对这把锁加锁，没抢到锁的线程继续阻塞。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 尝试加锁</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_mutex_trylock</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex)</span></span>;</span><br></pre></td></tr></table></figure>
<p>调用这个函数对互斥锁变量加锁还是有两种情况：<br>（1）如果这把锁没有被锁定，线程加锁成功；<br>（2）如果被锁住，调用这个函数加锁的线程不会被阻塞，加锁失败直接返回错误号。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对互斥锁解锁</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_mutex_unlock</span><span class="params">(<span class="type">pthread_mutex_t</span> *mutex)</span></span>;</span><br></pre></td></tr></table></figure>
<p>不是所有的线程都可以对互斥锁解锁，哪个线程加的锁，哪个线程才能解锁成功。</p>
<h3 id="互斥锁使用"><a href="#互斥锁使用" class="headerlink" title="互斥锁使用"></a>互斥锁使用</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX 50</span></span><br><span class="line"><span class="comment">// 全局变量</span></span><br><span class="line"><span class="type">int</span> number;</span><br><span class="line"><span class="comment">// 互斥锁</span></span><br><span class="line"><span class="comment">// 全局变量，多个线程共享</span></span><br><span class="line"><span class="type">pthread_mutex_t</span> mutex;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">funcA</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++) </span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        <span class="type">int</span> cur = number;</span><br><span class="line">        cur ++;</span><br><span class="line">        <span class="built_in">usleep</span>(<span class="number">10</span>);</span><br><span class="line">        number = cur;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Thread id = %lu, number = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), number);</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;mutex);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">funcB</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        <span class="type">int</span> cur = number;</span><br><span class="line">        cur ++;</span><br><span class="line">        number = cur;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Thread id = %lu, number = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), number);</span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;mutex);</span><br><span class="line">        <span class="built_in">usleep</span>(<span class="number">5</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">const</span> <span class="type">char</span>* argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">pthread_t</span> p1, p2;</span><br><span class="line">    <span class="comment">// 互斥锁初始化</span></span><br><span class="line">    <span class="built_in">pthread_mutex_init</span>(&amp;mutex, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建两个子线程</span></span><br><span class="line">    <span class="built_in">pthread_create</span>(&amp;p1, <span class="literal">NULL</span>, funcA, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">pthread_create</span>(&amp;p2, <span class="literal">NULL</span>, funcB, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞，资源回收</span></span><br><span class="line">    <span class="built_in">pthread_join</span>(p1, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">pthread_join</span>(p2, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 互斥锁释放</span></span><br><span class="line">    <span class="built_in">pthread_mutex_destroy</span>(&amp;mutex);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// gcc .\counter2.c -lpthread -o app</span></span><br></pre></td></tr></table></figure>

<h2 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h2><p>当多个线程访问共享资源需要加锁，如果锁使用不当，就会造成死锁这种现象。线程死锁造成的后果：所有的线程都被阻塞，并且线程的阻塞是无法解除的（因为可以解锁的线程也被阻塞）。<br>造成死锁的场景有如下几种：<br>（1）加锁之后忘记解锁</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 场景1</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">6</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 当前线程 A 加锁成功，当前循环完毕没有解锁，在下一轮循环的时候自己被阻塞</span></span><br><span class="line">        <span class="comment">// 其余的线程也被阻塞</span></span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">        <span class="comment">// 忘记解锁</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 场景2</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">6</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 当前线程 A 加锁成功</span></span><br><span class="line">        <span class="comment">// 其余的线程被阻塞</span></span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">        <span class="keyword">if</span> (xxx)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 函数退出, 没有解锁（解锁函数无法被执行了）</span></span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>（2）重复加锁，造成死锁</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">6</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 当前线程 A 加锁成功</span></span><br><span class="line">        <span class="comment">// 其余的线程阻塞</span></span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        <span class="comment">// 锁被锁住，A线程阻塞</span></span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;mutex);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 隐藏的比较深的情况</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">funcA</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">6</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 当前线程 A 加锁成功</span></span><br><span class="line">        <span class="comment">// 其余的线程阻塞</span></span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;mutex);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">funcB</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">6</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 当前线程A加锁成功</span></span><br><span class="line">        <span class="comment">// 其余的线程阻塞</span></span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        <span class="built_in">funcA</span>();  <span class="comment">// 重复加锁</span></span><br><span class="line">        <span class="comment">// ....</span></span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;mutex);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>（3）在程序中有多个共享资源，因此有很多把锁，随意加锁，导致相互被阻塞</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">场景描述:</span><br><span class="line">1.有两个共享资源 X、Y（X 对应锁 A、Y 对应锁 B）</span><br><span class="line">    - 线程 A 访问资源 X，加锁 A</span><br><span class="line">    - 线程 B 访问资源 Y，加锁 B</span><br><span class="line">2.线程 A 要访问资源 Y，线程 B 要访问资源 X（因为资源 X 和 Y 已经被对应的锁锁住了，因此这两个线程被阻塞）</span><br><span class="line">    - 线程 A 被锁 B 阻塞，无法打开 A 锁</span><br><span class="line">    - 线程 B 被锁 A 阻塞，无法打开 B 锁</span><br></pre></td></tr></table></figure>
<p>在使用多线程编程的时候，如何避免死锁？<br>（1）避免多次锁定，多检查<br>（2）对共享资源访问完毕之后，一定要解锁（或者在加锁的使用 trylock）<br>（3）如果程序中有多把锁，可以控制对锁的访问顺序（顺序访问共享资源，但有些情况下做不到）。另外在对其他互斥锁做加锁操作之前，先释放当前线程拥有的互斥锁。<br>（4）项目程序中可以引入一些专门用于死锁检测的模块</p>
<h2 id="读写锁"><a href="#读写锁" class="headerlink" title="读写锁"></a>读写锁</h2><h3 id="读写锁函数"><a href="#读写锁函数" class="headerlink" title="读写锁函数"></a>读写锁函数</h3><p>读写锁是互斥锁的升级版，读操作的时候可以提高程序的执行效率。<br>如果所有的线程都是做读操作，那么读是并行的。但是使用互斥锁，读操作也是串行的。</p>
<p>读写锁是一把锁，锁的类型为 <code>pthread_rwlock_t</code>，有了类型之后就可以创建一把互斥锁。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">pthread_rwlock_t</span> rwlock;</span><br></pre></td></tr></table></figure>
<p>称为读写锁，是因为这把锁既可以锁定读操作，也可以锁定写操作。为了方便理解，可以大致认为在这把锁中记录了这些信息：<br>（1）锁的状态：锁定/打开<br>（2）锁定的操作：读操作/写操作（使用读写锁锁定了读操作，需要先解锁才能去锁定写操作）<br>（3）哪个线程将这把锁锁上了</p>
<p>读写锁的使用方式，和互斥锁的使用方式是完全相同的：<br>（1）找共享资源，确定临界区。<br>（2）在临界区的开始位置加锁（读锁/写锁），临界区的结束位置解锁。</p>
<p>读写锁可以锁定读或者写操作，读写锁的特点如下：<br>（1）使用读写锁的读锁锁定了临界区，线程对临界区的访问是并行的，读锁是共享的。<br>（2）使用读写锁的写锁锁定了临界区，线程对临界区的访问是串行的，写锁是独占的。<br>（3）使用读写锁分别对两个临界区加了读锁和写锁，两个线程同时访问两个临界区，访问写锁临界区的线程继续运行，访问读锁临界区的线程阻塞，因为写锁比读锁的优先级高。</p>
<p>如果程序中所有的线程都对共享资源做写操作，使用读写锁没有优势，和互斥锁是一样的；<br>如果程序中所有的线程都对共享资源写操作/读操作，并且对共享资源读的操作越多，读写锁更有优势。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="type">pthread_rwlock_t</span> rwlock;</span><br><span class="line"><span class="comment">// 初始化读写锁</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_rwlock_init</span><span class="params">(<span class="type">pthread_rwlock_t</span> *restrict rwlock,</span></span></span><br><span class="line"><span class="params"><span class="function">           <span class="type">const</span> <span class="type">pthread_rwlockattr_t</span> *restrict attr)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 释放读写锁占用的系统资源</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_rwlock_destroy</span><span class="params">(<span class="type">pthread_rwlock_t</span> *rwlock)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    rwlock: </span></span><br><span class="line"><span class="comment">        读写锁的地址，传出参数</span></span><br><span class="line"><span class="comment">    attr: </span></span><br><span class="line"><span class="comment">        读写锁属性，一般使用默认属性，指定为 NULL</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在程序中对读写锁加读锁, 锁定的是读操作</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_rwlock_rdlock</span><span class="params">(<span class="type">pthread_rwlock_t</span> *rwlock)</span></span>;</span><br></pre></td></tr></table></figure>
<p>调用这个函数：<br>如果读写锁是打开的，那么加锁成功；<br>如果读写锁已经锁定了读操作，调用这个函数依然可以加锁成功，因为读锁是共享的；<br>如果读写锁已经锁定了写操作，调用这个函数的线程会被阻塞。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这个函数可以有效的避免死锁</span></span><br><span class="line"><span class="comment">// 如果加读锁失败, 不会阻塞当前线程, 直接返回错误号</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_rwlock_tryrdlock</span><span class="params">(<span class="type">pthread_rwlock_t</span> *rwlock)</span></span>;</span><br></pre></td></tr></table></figure>
<p>调用这个函数：<br>如果读写锁是打开的，那么加锁成功；<br>如果读写锁已经锁定了读操作，调用这个函数依然可以加锁成功，因为读锁是共享的；<br>如果读写锁已经锁定了写操作，调用这个函数加锁失败，对应的线程不会被阻塞，可以在程序中对函数返回值进行判断，添加加锁失败之后的处理动作。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在程序中对读写锁加写锁, 锁定的是写操作</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_rwlock_wrlock</span><span class="params">(<span class="type">pthread_rwlock_t</span> *rwlock)</span></span>;</span><br></pre></td></tr></table></figure>
<p>调用这个函数：<br>如果读写锁是打开的，那么加锁成功；<br>如果读写锁已经锁定了读操作/写操作，调用这个函数的线程会被阻塞。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这个函数可以有效的避免死锁</span></span><br><span class="line"><span class="comment">// 如果加写锁失败, 不会阻塞当前线程, 直接返回错误号</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_rwlock_trywrlock</span><span class="params">(<span class="type">pthread_rwlock_t</span> *rwlock)</span></span>;</span><br></pre></td></tr></table></figure>
<p>调用这个函数：<br>如果读写锁是打开的，那么加锁成功；<br>如果读写锁已经锁定了读操作/写操作，调用这个函数加锁失败，但是线程不会阻塞，可以在程序中对函数返回值进行判断，添加加锁失败之后的处理动作。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 解锁，锁定了读/写都可以解锁</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_rwlock_unlock</span><span class="params">(<span class="type">pthread_rwlock_t</span> *rwlock)</span></span>;</span><br></pre></td></tr></table></figure>

<h3 id="读写锁的使用"><a href="#读写锁的使用" class="headerlink" title="读写锁的使用"></a>读写锁的使用</h3><p>8 个线程操作同一个全局变量，3 个线程不定时写同一全局资源，5 个线程不定时读同一全局资源。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX 50</span></span><br><span class="line"><span class="comment">// 全局变量</span></span><br><span class="line"><span class="type">int</span> number;</span><br><span class="line"><span class="comment">// 读写锁</span></span><br><span class="line"><span class="type">pthread_rwlock_t</span> rwlock;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 线程处理函数</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">read_num</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_rwlock_rdlock</span>(&amp;rwlock);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Thread read, id = %lu, number = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), number);</span><br><span class="line">        <span class="built_in">pthread_rwlock_unlock</span>(&amp;rwlock);</span><br><span class="line">        <span class="built_in">usleep</span>(<span class="built_in">rand</span>() % <span class="number">5</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">write_num</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_rwlock_wrlock</span>(&amp;rwlock);</span><br><span class="line">        <span class="type">int</span> cur = number;</span><br><span class="line">        cur ++;</span><br><span class="line">        number = cur;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Thread write, id = %lu, number = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), number);</span><br><span class="line">        <span class="built_in">pthread_rwlock_unlock</span>(&amp;rwlock);</span><br><span class="line">        <span class="built_in">usleep</span>(<span class="built_in">rand</span>() % <span class="number">5</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">const</span> <span class="type">char</span>* argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;    </span><br><span class="line">    <span class="type">pthread_t</span> p1[<span class="number">5</span>], p2[<span class="number">3</span>];</span><br><span class="line">    <span class="built_in">pthread_rwlock_init</span>(&amp;rwlock, <span class="literal">NULL</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 创建两组子线程</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;p1[i], <span class="literal">NULL</span>, read_num, <span class="literal">NULL</span>);  <span class="comment">// 读线程 </span></span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3</span>; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;p2[i], <span class="literal">NULL</span>, write_num, <span class="literal">NULL</span>); <span class="comment">// 写线程</span></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 阻塞，回收资源</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_join</span>(p1[i], <span class="literal">NULL</span>);</span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3</span>; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_join</span>(p2[i], <span class="literal">NULL</span>);</span><br><span class="line">    &#125; </span><br><span class="line"></span><br><span class="line">    <span class="built_in">pthread_rwlock_destroy</span>(&amp;rwlock);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// gcc .\rwlock.c -lpthread -o app</span></span><br></pre></td></tr></table></figure>

<h2 id="条件变量"><a href="#条件变量" class="headerlink" title="条件变量"></a>条件变量</h2><p>严格意义上来说，条件变量的主要作用不是处理<strong>线程同步</strong>，而是进行<strong>线程的阻塞</strong>。<br>如果在多线程程序中只使用条件变量无法实现线程的同步，必须要配合<strong>互斥锁</strong>来使用。<br>虽然条件变量和互斥锁都能阻塞线程，但是二者的效果不一样，二者的区别如下：<br>（1）假设有 <code>A-Z</code> 26 个线程，这 26 个线程共同访问同一把互斥锁。如果线程 A 加锁成功，那么其余 B-Z 线程访问互斥锁都阻塞，所有的线程只能顺序访问临界区。<br>（2）条件变量只有在满足指定条件下才会阻塞线程。如果条件不满足，多个线程可以同时进入临界区，同时读写临界资源，这种情况下还是会出现共享资源中数据的混乱。</p>
<p>一般情况下，条件变量用于处理生产者和消费者模型，并且和互斥锁配合使用。<br>条件变量类型对应的类型为 <code>pthread_cond_t</code>，定义一个条件变量类型的变量：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">pthread_cond_t</span> cond;</span><br></pre></td></tr></table></figure>
<p>被条件变量阻塞的线程信息会被记录到这个变量中，在解除阻塞的时候使用。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="type">pthread_cond_t</span> cond;</span><br><span class="line"><span class="comment">// 初始化</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_cond_init</span><span class="params">(<span class="type">pthread_cond_t</span> *restrict cond,</span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="type">const</span> <span class="type">pthread_condattr_t</span> *restrict attr)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 销毁释放资源        </span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_cond_destroy</span><span class="params">(<span class="type">pthread_cond_t</span> *cond)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    cond: </span></span><br><span class="line"><span class="comment">        条件变量的地址</span></span><br><span class="line"><span class="comment">    attr: </span></span><br><span class="line"><span class="comment">        条件变量属性，一般使用默认属性，指定为 NULL</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 线程阻塞函数：哪个线程调用这个函数，哪个线程就会被阻塞</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_cond_wait</span><span class="params">(<span class="type">pthread_cond_t</span> *restrict cond, <span class="type">pthread_mutex_t</span> *restrict mutex)</span></span>;</span><br></pre></td></tr></table></figure>
<p>通过函数原型可知，该函数在阻塞线程的时候，需要一个互斥锁参数。<br>这个互斥锁主要功能是进行线程同步，让线程顺序进入临界区，避免出现数共享资源的数据混乱。<br>该函数会对这个互斥锁做以下几件事情：<br>（1）阻塞线程时候，如果线程已经对互斥锁 <code>mutex</code> 上锁，那么会将这把锁打开，这样做是为了避免死锁。<br>（2）线程解除阻塞的时候，函数内部会帮助这个线程再次将这个 <code>mutex</code> 互斥锁锁上，继续向下访问临界区。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 表示的时间是从 1971.1.1 到某个时间点的时间，总长度使用秒/纳秒表示</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">timespec</span> &#123;</span><br><span class="line">    <span class="type">time_t</span> tv_sec;   <span class="comment">/* Seconds */</span></span><br><span class="line">    <span class="type">long</span>   tv_nsec;  <span class="comment">/* Nanoseconds [0 .. 999999999] */</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 将线程阻塞一定的时间长度，时间到达之后，线程就解除阻塞</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_cond_timedwait</span><span class="params">(<span class="type">pthread_cond_t</span> *restrict cond,</span></span></span><br><span class="line"><span class="params"><span class="function">           <span class="type">pthread_mutex_t</span> *restrict mutex, <span class="type">const</span> <span class="keyword">struct</span> timespec *restrict abstime)</span></span>;</span><br></pre></td></tr></table></figure>
<p>这个函数的前两个参数和 <code>pthread_cond_wait</code> 函数是一样的，第三个参数表示线程阻塞的时长。<br>注意：struct timespec 这个结构体中记录的时间是从 1971.1.1 到某个时间点的时间，总长度使用秒/纳秒表示，因此赋值方式相对要麻烦一点：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">time_t</span> mytim = <span class="built_in">time</span>(<span class="literal">NULL</span>); <span class="comment">// 1970.1.1 0:0:0 到当前的总秒数</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">timespec</span> tmsp;</span><br><span class="line">tmsp.tv_nsec = <span class="number">0</span>;</span><br><span class="line">tmsp.tv_sec = <span class="built_in">time</span>(<span class="literal">NULL</span>) + <span class="number">100</span>;	<span class="comment">// 线程阻塞 100s</span></span><br></pre></td></tr></table></figure>

<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 唤醒阻塞在条件变量上的线程，至少有一个被解除阻塞</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_cond_signal</span><span class="params">(<span class="type">pthread_cond_t</span> *cond)</span></span>;</span><br><span class="line"><span class="comment">// 唤醒阻塞在条件变量上的线程，被阻塞的线程全部解除阻塞</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_cond_broadcast</span><span class="params">(<span class="type">pthread_cond_t</span> *cond)</span></span>;</span><br></pre></td></tr></table></figure>
<p>调用上面两个函数中的任意一个，都可以唤醒被 <code>pthread_cond_wait</code> 或者 <code>pthread_cond_timedwait</code> 阻塞的线程。<br>区别在于：<code>pthread_cond_signal</code> 是唤醒至少一个被阻塞的线程（总个数不定），<code>pthread_cond_broadcast</code> 是唤醒所有被阻塞的线程。</p>
<h3 id="生产者和消费者"><a href="#生产者和消费者" class="headerlink" title="生产者和消费者"></a>生产者和消费者</h3><p>生产者和消费者模型的组成：<br>（1）生产者线程（若干个）<br>生产商品或者任务放入到任务队列中<br>任务队列<strong>满了</strong>就阻塞，<strong>不满</strong>的时候就工作<br>通过一个<strong>生产者的条件变量</strong>控制生产者线程阻塞和非阻塞<br>（2）消费者线程（若干个）<br>读任务队列，将任务或者数据取出<br>任务队列中<strong>有数据</strong>就消费，<strong>没有数据</strong>就阻塞<br>通过一个<strong>消费者的条件变量</strong>控制消费者线程阻塞和非阻塞<br>（3）队列（存储任务/数据）<br>对应一块内存，为了读写访问可以通过一个数据结构维护这块内存<br>可以是数组、链表，也可以使用 <code>STL</code> 容器（queue/stack/list/vector）</p>
<p>场景描述：使用条件变量实现生产者和消费者模型。<br>生产者有 5 个，往链表头部添加节点；消费者也有 5 个，删除链表头部的节点。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX 5</span></span><br><span class="line"></span><br><span class="line"><span class="type">pthread_cond_t</span> cond;</span><br><span class="line"><span class="type">pthread_mutex_t</span> mutex;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 链表的节点类型</span></span><br><span class="line"><span class="comment">// 链表存放，不考虑仓库大小</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span> </span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> val;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Node</span>* next;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 链表头节点</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span>* head = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生产者</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">producer</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 创建新节点</span></span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">Node</span>* newNode = (<span class="keyword">struct</span> Node*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="keyword">struct</span> Node));</span><br><span class="line">        <span class="comment">// 初始化节点</span></span><br><span class="line">        newNode-&gt;val = <span class="built_in">rand</span>() % <span class="number">1000</span>;</span><br><span class="line">        newNode-&gt;next = head;</span><br><span class="line">        head = newNode;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;producer thread, id = %lu, val = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), newNode-&gt;val); </span><br><span class="line">        </span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;mutex);</span><br><span class="line">        <span class="comment">// 只要生产一个，就通知消费者去消费</span></span><br><span class="line">        <span class="comment">// pthread_cond_signal(&amp;cond);</span></span><br><span class="line">        <span class="built_in">pthread_cond_broadcast</span>(&amp;cond);</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="built_in">rand</span>() % <span class="number">3</span>); </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 消费者</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">consumer</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">while</span> (head == <span class="literal">NULL</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 阻塞消费者线程，阻塞会释放 mutex</span></span><br><span class="line">            <span class="built_in">pthread_cond_wait</span>(&amp;cond, &amp;mutex); </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">Node</span>* node = head;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;consumer thread, id = %lu, val = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), node-&gt;val);</span><br><span class="line">        head = head-&gt;next;</span><br><span class="line">        <span class="built_in">free</span>(node);</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;mutex);</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="built_in">rand</span>() % <span class="number">3</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;    </span><br><span class="line">    <span class="built_in">pthread_mutex_init</span>(&amp;mutex, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">pthread_cond_init</span>(&amp;cond, <span class="literal">NULL</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="type">pthread_t</span> t1[MAX], t2[MAX]; <span class="comment">// 生产者、消费者</span></span><br><span class="line">    <span class="comment">// 创建线程</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++) </span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;t1[i], <span class="literal">NULL</span>, producer, <span class="literal">NULL</span>);</span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;t2[i], <span class="literal">NULL</span>, consumer, <span class="literal">NULL</span>);</span><br><span class="line">    &#125; </span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 回收线程</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_join</span>(t1[i], <span class="literal">NULL</span>);</span><br><span class="line">        <span class="built_in">pthread_join</span>(t2[i], <span class="literal">NULL</span>);</span><br><span class="line">    &#125; </span><br><span class="line">    </span><br><span class="line">    <span class="built_in">pthread_mutex_destroy</span>(&amp;mutex);</span><br><span class="line">    <span class="built_in">pthread_cond_destroy</span>(&amp;cond);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="信号量"><a href="#信号量" class="headerlink" title="信号量"></a>信号量</h2><h3 id="信号量函数"><a href="#信号量函数" class="headerlink" title="信号量函数"></a>信号量函数</h3><p>信号量用在多线程多任务同步的，一个线程完成了某一个动作就通过信号量告诉别的线程，别的线程再执行某些动作。<br>信号量不一定是锁定某一个资源，而是流程上的概念。比如：有 A、B 两个线程，B 线程要等 A 线程完成某一任务以后再进行自己下面的步骤，这个任务并不一定是锁定某一资源，还可以是进行一些计算或者数据处理等。</p>
<p>信号量（信号灯）与互斥锁和条件变量的主要不同在于“灯” 的概念，灯亮则表示着资源可用，灯灭则表示资源不可用。<br>信号量主要<strong>阻塞线程</strong>，不能完全保证线程安全。如果要保证线程安全，需要信号量和<strong>互斥锁</strong>一起使用。<br>信号量和条件变量一样用于处理生产者和消费者模型，用于阻塞生产者线程或者消费者线程的运行。</p>
<p>信号的类型为 <code>sem_t</code>：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;semaphore.h&gt;</span></span></span><br><span class="line"><span class="type">sem_t</span> sem;</span><br></pre></td></tr></table></figure>

<p>Linux 提供的信号量操作函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;semaphore.h&gt;</span></span></span><br><span class="line"><span class="comment">// 初始化信号量/信号灯</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sem_init</span><span class="params">(<span class="type">sem_t</span> *sem, <span class="type">int</span> pshared, <span class="type">unsigned</span> <span class="type">int</span> value)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数:</span></span><br><span class="line"><span class="comment">    sem：</span></span><br><span class="line"><span class="comment">        信号量变量地址</span></span><br><span class="line"><span class="comment">    pshared：</span></span><br><span class="line"><span class="comment">        0：线程同步</span></span><br><span class="line"><span class="comment">        非 0：进程同步</span></span><br><span class="line"><span class="comment">    value：</span></span><br><span class="line"><span class="comment">        初始化当前信号量拥有的资源数（&gt;=0）。如果资源数为 0，线程就会被阻塞。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 资源释放，线程销毁之后调用这个函数</span></span><br><span class="line"><span class="comment">// 参数 sem 是 sem_init() 的第一个参数            </span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sem_destroy</span><span class="params">(<span class="type">sem_t</span> *sem)</span></span>;</span><br></pre></td></tr></table></figure>

<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数 sem 是 sem_init() 的第一个参数  </span></span><br><span class="line"><span class="comment">// 函数被调用 sem 中的资源就会被消耗 1 个，资源数 -1</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sem_wait</span><span class="params">(<span class="type">sem_t</span> *sem)</span></span>;</span><br></pre></td></tr></table></figure>
<p>当线程调用这个函数，并且 sem 中的资源数 &gt; 0，线程不会阻塞，线程会占用 sem 中的一个资源，因此资源数 -1，<br>直到 sem 中的资源数减为 0 时，资源被耗尽，因此线程也就被阻塞。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数 sem 就是 sem_init() 的第一个参数  </span></span><br><span class="line"><span class="comment">// 函数被调用 sem 中的资源就会被消耗 1 个，资源数-1</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sem_trywait</span><span class="params">(<span class="type">sem_t</span> *sem)</span></span>;</span><br></pre></td></tr></table></figure>
<p>当线程调用这个函数，并且 sem 中的资源数 &gt; 0，线程不会阻塞，线程会占用 sem 中的一个资源，因此资源数 -1，<br>直到 sem 中的资源数减为 0 时，资源被耗尽，但是线程不会被阻塞，直接返回错误号。<br>因此可以在程序中添加判断分支，用于处理获取资源失败之后的情况。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 调用该函数给 sem 中的资源数 +1</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sem_post</span><span class="params">(<span class="type">sem_t</span> *sem)</span></span>;</span><br></pre></td></tr></table></figure>
<p>调用该函数会将 sem 中的资源数 +1，<br>如果有线程在调用 <code>sem_wait</code>、<code>sem_trywait</code>、<code>sem_timedwait</code> 时因为 sem 中的资源数为 0 被阻塞，这时这些线程会解除阻塞，获取到资源之后继续向下运行。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 查看信号量 sem 中的整形数的当前值，这个值会被写入到 sval 指针对应的内存中</span></span><br><span class="line"><span class="comment">// sval 是一个传出参数</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sem_getvalue</span><span class="params">(<span class="type">sem_t</span> *sem, <span class="type">int</span> *sval)</span></span>;</span><br></pre></td></tr></table></figure>
<p>通过这个函数可以查看 sem 中现在拥有的资源个数，通过第二个参数 sval 将数据传出，第二个参数的作用和返回值是一样的。</p>
<h3 id="生产者和消费者-1"><a href="#生产者和消费者-1" class="headerlink" title="生产者和消费者"></a>生产者和消费者</h3><p>由于生产者和消费者是两类线程，并且在还没有生成之前是不能进行消费的，<br>在使用信号量处理这类问题的时候可以定义两个信号量，分别用于记录生产者和消费者线程拥有的总资源数。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 生产者线程 </span></span><br><span class="line"><span class="type">sem_t</span> psem;</span><br><span class="line"><span class="comment">// 消费者线程</span></span><br><span class="line"><span class="type">sem_t</span> csem;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 信号量初始化</span></span><br><span class="line"><span class="built_in">sem_init</span>(&amp;psem, <span class="number">0</span>, <span class="number">5</span>);   <span class="comment">// 5个生产者可以同时生产</span></span><br><span class="line"><span class="built_in">sem_init</span>(&amp;csem, <span class="number">0</span>, <span class="number">0</span>);   <span class="comment">// 消费者线程没有资源，因此不能消费</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 生产者线程</span></span><br><span class="line"><span class="comment">// 在生产之前，从信号量中取出一个资源</span></span><br><span class="line"><span class="built_in">sem_wait</span>(&amp;psem);	</span><br><span class="line"><span class="comment">// 生产者商品代码，有商品了，放到任务队列</span></span><br><span class="line"><span class="comment">// ...	 </span></span><br><span class="line"><span class="comment">// 通知消费者消费，给消费者信号量添加资源，让消费者解除阻塞</span></span><br><span class="line"><span class="built_in">sem_post</span>(&amp;csem);</span><br><span class="line">    </span><br><span class="line"><span class="comment">// -----------------------------------------------------</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 消费者线程</span></span><br><span class="line"><span class="comment">// 消费者需要等待生产，默认启动之后应该阻塞</span></span><br><span class="line"><span class="built_in">sem_wait</span>(&amp;csem);</span><br><span class="line"><span class="comment">// 开始消费</span></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="comment">// 消费完成，通过生产者生产，给生产者信号量添加资源</span></span><br><span class="line"><span class="built_in">sem_post</span>(&amp;psem);</span><br></pre></td></tr></table></figure>

<h3 id="信号量使用"><a href="#信号量使用" class="headerlink" title="信号量使用"></a>信号量使用</h3><p>场景描述：使用信号量实现生产者和消费者模型。<br>生产者有 5 个，往链表头部添加节点；消费者也有 5 个，删除链表头部的节点。</p>
<h4 id="总资源数为-1"><a href="#总资源数为-1" class="headerlink" title="总资源数为 1"></a>总资源数为 1</h4><p>如果生产者和消费者线程使用的信号量对应的总资源数为 1，<br>那么不管线程有多少个，可以工作的线程只有一个，其余线程由于拿不到资源都被迫阻塞。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;semaphore.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX 5</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 生产者的信号量</span></span><br><span class="line"><span class="type">sem_t</span> semp;</span><br><span class="line"><span class="comment">// 消费者的信号量</span></span><br><span class="line"><span class="type">sem_t</span> semc;</span><br><span class="line"><span class="type">pthread_mutex_t</span> mutex;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 链表的节点类型</span></span><br><span class="line"><span class="comment">// 链表存放，不考虑仓库大小</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span> </span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> val;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Node</span>* next;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 链表头节点</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span>* head = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生产者</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">producer</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sem_wait</span>(&amp;semp);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建新节点</span></span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">Node</span>* newNode = (<span class="keyword">struct</span> Node*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="keyword">struct</span> Node));</span><br><span class="line">        <span class="comment">// 初始化节点</span></span><br><span class="line">        newNode-&gt;val = <span class="built_in">rand</span>() % <span class="number">1000</span>;</span><br><span class="line">        newNode-&gt;next = head;</span><br><span class="line">        head = newNode;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;producer thread, id = %lu, val = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), newNode-&gt;val);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">sem_post</span>(&amp;semc);</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="built_in">rand</span>() % <span class="number">3</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 消费者</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">consumer</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sem_wait</span>(&amp;semc);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">Node</span>* node = head;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;consumer thread, id = %lu, val = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), node-&gt;val);</span><br><span class="line">        head = head-&gt;next;</span><br><span class="line">        <span class="built_in">free</span>(node);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">sem_post</span>(&amp;semp);</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="built_in">rand</span>() % <span class="number">3</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;    </span><br><span class="line">    <span class="comment">// 生产者</span></span><br><span class="line">    <span class="built_in">sem_init</span>(&amp;semp, <span class="number">0</span>, <span class="number">1</span>);</span><br><span class="line">    <span class="comment">// 消费者（资源初始化为 0，消费者线程启动就阻塞）</span></span><br><span class="line">    <span class="built_in">sem_init</span>(&amp;semc, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="type">pthread_t</span> t1[MAX], t2[MAX]; <span class="comment">// 生产者、消费者</span></span><br><span class="line">    <span class="comment">// 创建线程</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;t1[i], <span class="literal">NULL</span>, producer, <span class="literal">NULL</span>);</span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;t2[i], <span class="literal">NULL</span>, consumer, <span class="literal">NULL</span>);</span><br><span class="line">    &#125; </span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 回收线程</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; i ++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_join</span>(t1[i], <span class="literal">NULL</span>);</span><br><span class="line">        <span class="built_in">pthread_join</span>(t2[i], <span class="literal">NULL</span>);</span><br><span class="line">    &#125; </span><br><span class="line">    </span><br><span class="line">    <span class="built_in">pthread_mutex_destroy</span>(&amp;mutex);</span><br><span class="line">    <span class="built_in">sem_destroy</span>(&amp;semp);</span><br><span class="line">    <span class="built_in">sem_destroy</span>(&amp;semc); </span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过测试代码得到如下结论：<br>如果生产者和消费者使用的信号量总资源数为 1，那么不会出现生产者线程和消费者线程同时访问共享资源的情况。不管生产者和消费者线程有多少个，它们都是顺序执行的。</p>
<h4 id="总资源数大于-1"><a href="#总资源数大于-1" class="headerlink" title="总资源数大于 1"></a>总资源数大于 1</h4><p>如果生产者和消费者线程使用的信号量对应的总资源数为大于 1，这种场景下出现的情况就比较多：<br>（1）多个生产者线程同时生产<br>（2）多个消费者同时消费<br>（3）生产者线程和消费者线程同时生产和消费<br>以上不管哪一种情况都可能会出现多个线程访问共享资源的情况，<br>如果要防止共享资源出现数据混乱，那么需要使用互斥锁进行线程同步，处理代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;semaphore.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAX 5</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 生产者的信号量</span></span><br><span class="line"><span class="type">sem_t</span> semp;</span><br><span class="line"><span class="comment">// 消费者的信号量</span></span><br><span class="line"><span class="type">sem_t</span> semc;</span><br><span class="line"><span class="type">pthread_mutex_t</span> mutex;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 链表的节点类型</span></span><br><span class="line"><span class="comment">// 链表存放，不考虑仓库大小</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span> </span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> val;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Node</span>* next;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 链表头节点</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span>* head = <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生产者</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">producer</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sem_wait</span>(&amp;semp);</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建新节点</span></span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">Node</span>* newNode = (<span class="keyword">struct</span> Node*)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="keyword">struct</span> Node));</span><br><span class="line">        <span class="comment">// 初始化节点</span></span><br><span class="line">        newNode-&gt;val = <span class="built_in">rand</span>() % <span class="number">1000</span>;</span><br><span class="line">        newNode-&gt;next = head;</span><br><span class="line">        head = newNode;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;producer thread, id = %lu, val = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), newNode-&gt;val);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;mutex);</span><br><span class="line">        <span class="built_in">sem_post</span>(&amp;semc);</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="built_in">rand</span>() % <span class="number">3</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 消费者</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">consumer</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sem_wait</span>(&amp;semc);</span><br><span class="line">        <span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">Node</span>* node = head;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;consumer thread, id = %lu, val = %d\n&quot;</span>, <span class="built_in">pthread_self</span>(), node-&gt;val);</span><br><span class="line">        head = head-&gt;next;</span><br><span class="line">        <span class="built_in">free</span>(node);</span><br><span class="line"></span><br><span class="line">        <span class="built_in">pthread_mutex_unlock</span>(&amp;mutex);</span><br><span class="line">        <span class="built_in">sem_post</span>(&amp;semp);</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="built_in">rand</span>() % <span class="number">3</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 生产者</span></span><br><span class="line">    <span class="built_in">sem_init</span>(&amp;semp, <span class="number">0</span>, <span class="number">5</span>);</span><br><span class="line">    <span class="comment">// 消费者（资源初始化为 0，消费者线程启动就阻塞）</span></span><br><span class="line">    <span class="built_in">sem_init</span>(&amp;semc, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="built_in">pthread_mutex_init</span>(&amp;mutex, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">pthread_t</span> t1[MAX], t2[MAX]; <span class="comment">// 生产者、消费者</span></span><br><span class="line">    <span class="comment">// 创建线程</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;t1[i], <span class="literal">NULL</span>, producer, <span class="literal">NULL</span>);</span><br><span class="line">        <span class="built_in">pthread_create</span>(&amp;t2[i], <span class="literal">NULL</span>, consumer, <span class="literal">NULL</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 回收线程  </span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; MAX; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">pthread_join</span>(t1[i], <span class="literal">NULL</span>);</span><br><span class="line">        <span class="built_in">pthread_join</span>(t2[i], <span class="literal">NULL</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">pthread_mutex_destroy</span>(&amp;mutex);</span><br><span class="line">    <span class="built_in">sem_destroy</span>(&amp;semp);</span><br><span class="line">    <span class="built_in">sem_destroy</span>(&amp;semc);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编写上述代码的时候还有一个注意事项：不论是消费者线程的处理函数还是生产者线程的处理函数，内部有这两行代码：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 消费者</span></span><br><span class="line"><span class="built_in">sem_wait</span>(&amp;csem);</span><br><span class="line"><span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生产者</span></span><br><span class="line"><span class="built_in">sem_wait</span>(&amp;csem);</span><br><span class="line"><span class="built_in">pthread_mutex_lock</span>(&amp;mutex);</span><br></pre></td></tr></table></figure>
<p>这两行代码的调用顺序是不能颠倒的，如果颠倒过来就有可能会造成死锁。</p>
<p>下面分析一种死锁的场景：初始化状态下消费者线程没有任务信号量资源。<br>假设某一个消费者线程先运行，调用 <code>pthread_mutex_lock(&amp;mutex)</code> 对互斥锁加锁成功，然后调用 <code>sem_wait(&amp;csem)</code> 由于没有资源，因此被阻塞。其余的消费者线程由于没有抢到互斥锁，因此被阻塞在互斥锁上。<br>对应生产者线程第一步操作也是调用 <code>pthread_mutex_lock(&amp;mutex)</code>，但是这时候互斥锁已经被消费者线程锁上了，所有生产者都被阻塞。<br>到此为止，多余的线程都被阻塞了，程序产生了死锁。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a target="_blank" rel="noopener" href="https://subingwen.cn/linux/thread-sync/">https://subingwen.cn/linux/thread-sync/</a></p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">线程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/20/%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-线程" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/18/%E7%BA%BF%E7%A8%8B/">线程</a>
    </h1>
  

        
        
<a href="/2021/08/18/%E7%BA%BF%E7%A8%8B/" class="archive-article-date">
  	<time datetime="2021-08-18T03:06:47.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-18</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="线程的概念"><a href="#线程的概念" class="headerlink" title="线程的概念"></a>线程的概念</h2><p>线程是轻量级的进程（LWP：light weight process），在 Linux 环境下线程的本质仍是进程。<br>在计算机上运行的程序是一组指令及指令参数的组合，指令按照既定的逻辑控制计算机运行。<br>操作系统会以进程为单位，分配系统资源。进程是资源分配的最小单位，线程是操作系统调度执行的最小单位。</p>
<p>线程和进程之间的区别：<br>（1）线程是程序的最小执行单位，进程是操作系统中最小的资源分配单位。<br>每个进程对应一个虚拟地址空间，一个进程只能抢一个 CPU 时间片。<br>一个地址空间中划分出多个线程，能够抢更多的 CPU 时间片。<br>（2）进程有自己独立的地址空间，多个线程共用同一个地址空间。<br>线程更加节省系统资源，效率更高。<br>在一个地址空间中多个线程独享：每个线程都有属于自己的栈区、寄存器 (内核中管理的)<br>在一个地址空间中多个线程共享：代码段、全局数据区、堆区、打开的文件 (文件描述符表) 都是线程共享的<br>（3）CPU 的调度和切换：线程的上下文切换比进程快很多<br>上下文切换：进程/线程分时复用 CPU 时间片，在切换之前会将上一个任务的状态进行保存，<br>下次切换回这个任务的时候，加载这个状态继续运行，任务从保存到再次加载这个过程就是一次上下文切换。<br>（4）线程启动速度更快，退出也快，对系统资源的冲击小。</p>
<p>在处理多任务程序的时候使用多线程比使用多进程更有优势，<br>但是线程并不是越多越好，如何控制线程的个数呢？<br>（1）IO 密集型<br>文件 IO 对 CPU 是使用率不高，因此可以分时复用 CPU 时间片，线程的个数 = 2 * CPU 核心数（效率最高）<br>（2）CPU 密集型<br>主要是 CPU 进行运算，线程的个数 = CPU 的核心数（效率最高）</p>
<h2 id="创建线程"><a href="#创建线程" class="headerlink" title="创建线程"></a>创建线程</h2><h3 id="线程函数"><a href="#线程函数" class="headerlink" title="线程函数"></a>线程函数</h3><p>每一个线程都有一个唯一的线程 ID，ID 类型为 <code>pthread_t</code>，这个 ID 是一个无符号长整形数。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">pthread_t</span> <span class="title">pthread_self</span><span class="params">(<span class="type">void</span>)</span></span>; <span class="comment">// 返回当前线程的线程ID</span></span><br></pre></td></tr></table></figure>
<p>在一个进程中调用线程创建函数，就可得到一个子线程。<br>和进程不同，需要给每一个创建出的线程指定一个处理函数，否则这个线程无法工作。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_create</span><span class="params">(<span class="type">pthread_t</span> *thread, <span class="type">const</span> <span class="type">pthread_attr_t</span> *attr,</span></span></span><br><span class="line"><span class="params"><span class="function">                   <span class="type">void</span> *(*start_routine) (<span class="type">void</span> *), <span class="type">void</span> *arg)</span></span>;</span><br><span class="line"><span class="comment">// Compile and link with -pthread, 线程库的全名: libpthread.so libptread.a</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数:</span></span><br><span class="line"><span class="comment">    thread:</span></span><br><span class="line"><span class="comment">        传出参数，是无符号长整形数，线程创建成功，会将线程 ID 写入到这个指针指向的内存</span></span><br><span class="line"><span class="comment">    attr: </span></span><br><span class="line"><span class="comment">        线程的属性，一般情况下使用默认属性 NULL</span></span><br><span class="line"><span class="comment">    start_routine: </span></span><br><span class="line"><span class="comment">        函数指针，创建出的子线程的处理动作，也就是该函数在子线程中执行。</span></span><br><span class="line"><span class="comment">    arg: </span></span><br><span class="line"><span class="comment">        作为实参传递到 start_routine 指针指向的函数内部</span></span><br><span class="line"><span class="comment">    返回值：</span></span><br><span class="line"><span class="comment">        线程创建成功返回 0，创建失败返回对应的错误号</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>
<h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><p>创建线程的示例代码，<br>在创建过程中一定要保证编写的线程函数与规定的函数指针类型一致：<code>void* (*start_routine) (void*)</code></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// pthread_create.c </span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 子线程的处理代码</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">callback</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;child thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">9</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;child thread, i = %d\n&quot;</span>, i);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 1.创建一个子线程</span></span><br><span class="line">    <span class="type">pthread_t</span> tid;</span><br><span class="line">    <span class="built_in">pthread_create</span>(&amp;tid, <span class="literal">NULL</span>, callback, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.子线程不会执行下边的代码, 主线程执行</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;main thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">3</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;main thread, i = %d\n&quot;</span>, i);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 休息一会儿...</span></span><br><span class="line">    <span class="built_in">sleep</span>(<span class="number">1</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// gcc .\pthread_create.c -lpthread -o app</span></span><br></pre></td></tr></table></figure>
<p>在打印的输出中为什么子线程处理函数没有执行完毕呢（只看到了子线程的部分日志输出）？<br>主线程一直在运行，执行期间创建出了子线程，说明主线程有 CPU 时间片，在这个时间片内将代码执行完毕了，主线程就退出了。<br>子线程被创建出来之后需要抢 cpu 时间片, 抢不到就不能运行。如果主线程退出，虚拟地址空间就被释放，子线程一并被销毁。<br>但是如果某一个子线程退出，主线程仍在运行，虚拟地址空间依旧存在。</p>
<p>结论：在没有人为干预的情况下，虚拟地址空间的生命周期和主线程是一样的，与子线程无关。<br>解决方案：让子线程执行完毕，主线程再退出，可以在主线程中添加挂起函数 <code>sleep()</code>。</p>
<h2 id="线程退出"><a href="#线程退出" class="headerlink" title="线程退出"></a>线程退出</h2><h3 id="线程函数-1"><a href="#线程函数-1" class="headerlink" title="线程函数"></a>线程函数</h3><p>在编写多线程程序的时候，如果想要让线程退出，但是不会导致虚拟地址空间的释放（针对于主线程），<br>可以调用线程库中的线程退出函数，调用该函数当前线程就马上退出，并且不会影响到其他线程的正常运行，<br>不论是在子线程或者主线程中都可以使用。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pthread_exit</span><span class="params">(<span class="type">void</span> *retval)</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="示例-1"><a href="#示例-1" class="headerlink" title="示例"></a>示例</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// pthread_exit.c </span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 子线程的处理代码</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">callback</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;child thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">9</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;child thread, i = %d\n&quot;</span>, i);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 1.创建一个子线程</span></span><br><span class="line">    <span class="type">pthread_t</span> tid;</span><br><span class="line">    <span class="built_in">pthread_create</span>(&amp;tid, <span class="literal">NULL</span>, callback, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.子线程不会执行下边的代码, 主线程执行</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;main thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">    <span class="comment">// 主线程退出</span></span><br><span class="line">    <span class="built_in">pthread_exit</span>(<span class="literal">NULL</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// gcc .\pthread_exit.c -lpthread -o app</span></span><br></pre></td></tr></table></figure>
<p>主线程退出后，不影响子线程的执行。</p>
<h2 id="线程回收"><a href="#线程回收" class="headerlink" title="线程回收"></a>线程回收</h2><h3 id="线程函数-2"><a href="#线程函数-2" class="headerlink" title="线程函数"></a>线程函数</h3><p>线程和进程一样，子线程退出的时候其内核资源主要由主线程回收。<br>线程库中提供的线程回收函叫做 <code>pthread_join()</code>，这个函数是一个阻塞函数，<br>如果还有子线程在运行，调用该函数就会阻塞，子线程退出函数解除阻塞进行资源的回收，<br>函数被调用一次只能回收一个子线程，如果有多个子线程，则需要循环进行回收。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="comment">// 这是一个阻塞函数, 子线程在运行这个函数就阻塞</span></span><br><span class="line"><span class="comment">// 子线程退出，函数解除阻塞，回收对应的子线程资源，类似于回收进程使用的函数 wait()</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_join</span><span class="params">(<span class="type">pthread_t</span> thread, <span class="type">void</span> **retval)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    thread:</span></span><br><span class="line"><span class="comment">        要被回收的子线程的线程 ID</span></span><br><span class="line"><span class="comment">    retval: </span></span><br><span class="line"><span class="comment">        二级指针，指向一级指针的地址，是一个传出参数，</span></span><br><span class="line"><span class="comment">        这个地址中存储了 pthread_exit () 传递出的数据，如果不需要这个参数，可以指定为 NULL</span></span><br><span class="line"><span class="comment">    返回值：</span></span><br><span class="line"><span class="comment">        线程回收成功返回 0，回收失败返回错误号。</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>
<h3 id="示例-2"><a href="#示例-2" class="headerlink" title="示例"></a>示例</h3><p>在子线程退出的时候可以使用 <code>pthread_exit()</code> 的参数将数据传出，<br>在回收这个子线程的时候可以通过 <code>phread_join()</code> 的第二个参数来接收子线程传递出的数据。<br>接收数据有很多种处理方式，列举几种：<br>（1）使用子线程栈<br>将子线程退出数据保存在子线程自己的栈区。<br>如果多个线程共用同一个虚拟地址空间，每个线程在栈区都有一块属于自己的内存，相当于栈区被这几个线程平分。<br>当线程退出时，线程在栈区的内存也就被回收。因此，随着子线程的退出，写入到栈区的数据也就被释放。<br>（2）使用全局变量<br>位于同一虚拟地址空间中的线程，虽然不能共享栈区数据，但是可以共享全局数据区和堆区数据。<br>因此，在子线程退出的时可以将传出数据存储到全局变量、静态变量或者堆内存中。<br>（3）使用主线程栈<br>虽然每个线程都有属于自己的栈区空间，但是位于同一个地址空间的多个线程是可以相互访问对方的栈空间上的数据的。<br>由于很多情况下还需要在主线程中回收子线程资源，所以主线程一般都是最后退出，<br>基于这个原因将子线程返回的数据保存到了主线程的栈区内存中。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// pthread_join.c </span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Test</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> num;</span><br><span class="line">    <span class="type">int</span> age;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 子线程的处理代码</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">callback</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;child thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">9</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;child thread, i = %d\n&quot;</span>, i);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Test</span>* t = (<span class="keyword">struct</span> Test*)arg;</span><br><span class="line">    t-&gt;num = <span class="number">100</span>;</span><br><span class="line">    t-&gt;age = <span class="number">6</span>;</span><br><span class="line">    <span class="built_in">pthread_exit</span>(t);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Test</span> t;</span><br><span class="line">    <span class="comment">// 1.创建一个子线程</span></span><br><span class="line">    <span class="type">pthread_t</span> tid;</span><br><span class="line">    <span class="built_in">pthread_create</span>(&amp;tid, <span class="literal">NULL</span>, callback, &amp;t);</span><br><span class="line">    <span class="comment">// 2.子线程不会执行下边的代码, 主线程执行</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;main thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line"></span><br><span class="line">    <span class="type">void</span>* ptr;</span><br><span class="line">    <span class="built_in">pthread_join</span>(tid, &amp;ptr);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;num: %d, age = %d\n&quot;</span>, t.num, t.age);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// gcc .\pthread_join.c -lpthread -o app</span></span><br></pre></td></tr></table></figure>
<p>在上面的程序中，创建子线程并将主线程中栈空间变量 p 的地址传递到了子线程中，在子线程中将要传递出的数据写入到了这块内存中。<br>在程序的 <code>main()</code> 函数中，通过指针变量 ptr 或者通过结构体变量 p 都可以读出子线程传出的数据。</p>
<h2 id="线程分离"><a href="#线程分离" class="headerlink" title="线程分离"></a>线程分离</h2><h3 id="线程函数-3"><a href="#线程函数-3" class="headerlink" title="线程函数"></a>线程函数</h3><p>在某些情况下，程序中的主线程有属于自己的业务处理流程，如果让主线程负责子线程的资源回收，调用 <code>pthread_join()</code> 只要子线程不退出主线程就会一直被阻塞，主要线程的任务也就不能被执行。<br>在线程库函数中线程分离函数 <code>pthread_detach()</code>，调用这个函数之后指定的子线程和主线程分离，<br>当子线程退出的时候，其占用的内核资源就被系统的其他进程接管并回收。<br>线程分离之后在主线程中使用 <code>pthread_join()</code> 就回收不到子线程资源。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="comment">// 参数是子线程的线程ID, 主线程和这个子线程分离</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_detach</span><span class="params">(<span class="type">pthread_t</span> thread)</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="示例-3"><a href="#示例-3" class="headerlink" title="示例"></a>示例</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// pthread_detach.c </span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 子线程的处理代码</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">callback</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;child thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">9</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;child thread, i = %d\n&quot;</span>, i);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 1.创建一个子线程</span></span><br><span class="line">    <span class="type">pthread_t</span> tid;</span><br><span class="line">    <span class="built_in">pthread_create</span>(&amp;tid, <span class="literal">NULL</span>, callback, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="comment">// 2.子线程不会执行下边的代码, 主线程执行</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;main thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">3</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;main thread, i = %d\n&quot;</span>, i);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置子线程和主线程分离</span></span><br><span class="line">    <span class="built_in">pthread_detach</span>(tid);</span><br><span class="line">    <span class="comment">// 让主线程自己退出</span></span><br><span class="line">    <span class="built_in">pthread_exit</span>(<span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// gcc .\pthread_detach.c -lpthread -o app</span></span><br></pre></td></tr></table></figure>

<h2 id="其他线程函数"><a href="#其他线程函数" class="headerlink" title="其他线程函数"></a>其他线程函数</h2><h3 id="线程取消"><a href="#线程取消" class="headerlink" title="线程取消"></a>线程取消</h3><p>线程取消：在某些特定情况下在一个线程中杀死另一个线程。<br>使用这个函数杀死一个线程需要分两步：<br>（1）在线程 A 中调用线程取消函数 <code>pthread_cancel</code>，指定杀死线程 B，这时候线程 B 是死不了的。<br>（2）在线程 B 中进程一次系统调用（从用户区切换到内核区），否则线程 B 可以一直运行。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="comment">// 参数是子线程的线程ID</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_cancel</span><span class="params">(<span class="type">pthread_t</span> thread)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    参数：</span></span><br><span class="line"><span class="comment">        要杀死的线程的线程 ID</span></span><br><span class="line"><span class="comment">    返回值：</span></span><br><span class="line"><span class="comment">        函数调用成功返回 0，调用失败返回非 0 错误号。</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>
<p>示例</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 子线程的处理代码</span></span><br><span class="line"><span class="function"><span class="type">void</span>* <span class="title">working</span><span class="params">(<span class="type">void</span>* arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> j=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">9</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        j++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 这个函数会调用系统函数, 因此这是个间接的系统调用</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;child thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">9</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;child thread, i = %d\n&quot;</span>, i);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 1. 创建一个子线程</span></span><br><span class="line">    <span class="type">pthread_t</span> tid;</span><br><span class="line">    <span class="built_in">pthread_create</span>(&amp;tid, <span class="literal">NULL</span>, working, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2. 子线程不会执行下边的代码, 主线程执行</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;main thread, ID: %ld\n&quot;</span>, <span class="built_in">pthread_self</span>());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 杀死子线程, 如果子线程中做系统调用, 子线程就结束了</span></span><br><span class="line">    <span class="built_in">pthread_cancel</span>(tid);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 让主线程自己退出</span></span><br><span class="line">    <span class="built_in">pthread_exit</span>(<span class="literal">NULL</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// gcc .\pthread_cancel.c -lpthread -o app</span></span><br></pre></td></tr></table></figure>
<h3 id="线程ID比较"><a href="#线程ID比较" class="headerlink" title="线程ID比较"></a>线程ID比较</h3><p>在 Linux 中线程 ID 是一个无符号长整形，因此可以直接使用比较操作符比较两个线程的 ID。<br>但是线程库是可以跨平台使用的，在某些平台上 <code>pthread_t</code> 可能不是一个单纯的整形，这中情况下比较两个线程的 ID 必须要使用比较函数。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pthread_equal</span><span class="params">(<span class="type">pthread_t</span> t1, <span class="type">pthread_t</span> t2)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    参数：</span></span><br><span class="line"><span class="comment">        t1 和 t2 是要比较的线程的线程 ID</span></span><br><span class="line"><span class="comment">    返回值：</span></span><br><span class="line"><span class="comment">        如果两个线程 ID 相等返回非 0 值，如果不相等返回 0</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a target="_blank" rel="noopener" href="https://subingwen.cn/linux/thread/">https://subingwen.cn/linux/thread/</a></p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">线程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/18/%E7%BA%BF%E7%A8%8B/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-信号" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/14/%E4%BF%A1%E5%8F%B7/">信号</a>
    </h1>
  

        
        
<a href="/2021/08/14/%E4%BF%A1%E5%8F%B7/" class="archive-article-date">
  	<time datetime="2021-08-14T04:46:05.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-14</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>Linux 中的信号是一种消息处理机制，它本质上是一个整数，不同的信号对应不同的值，由于信号的结构简单所以不能携带很大的信息量，但是信号在系统中的优先级是非常高的。</p>
<p>在 Linux 中的很多常规操作中都会有相关的信号产生，先从熟悉的场景说起：<br>（1）通过键盘操作产生了信号<br>用户按下 <code>Ctrl-C</code>，这个键盘输入产生一个硬件中断，使用这个快捷键会产生信号，这个信号会杀死对应的某个进程<br>（2）通过 <code>shell</code> 命令产生了信号<br>通过 <code>kill</code> 命令终止某一个进程，<code>kill -9 进程PID</code><br>（3）通过函数调用产生了信号<br>如果 CPU 当前正在执行这个进程的代码调用（如：函数 <code>sleep()</code> 进程收到相关的信号，被迫挂起）<br>（4）通过对硬件进行非法访问产生了信号<br>正在运行的程序访问了非法内存，发生段错误，进程退出。</p>
<p>信号也可以实现进程间通信，但是信号能传递的数据量很少，不能满足大部分需求。另外信号的优先级很高，并且它对应的处理动作是回调完成的，它会打乱程序原有的处理流程，影响到最终的处理结果。因此，不建议使用信号进行进程间通信。</p>
<h2 id="信号"><a href="#信号" class="headerlink" title="信号"></a>信号</h2><h3 id="信号编号"><a href="#信号编号" class="headerlink" title="信号编号"></a>信号编号</h3><p>通过 <code>kill -l</code> 命令查看系统定义的信号列表：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 执行shell命令查看信号</span></span><br><span class="line">$ <span class="built_in">kill</span> -l</span><br><span class="line"> 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP</span><br><span class="line"> 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1</span><br><span class="line">11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM</span><br><span class="line">16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP</span><br><span class="line">21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ</span><br><span class="line">26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR</span><br><span class="line">31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3</span><br><span class="line">38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8</span><br><span class="line">43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13</span><br><span class="line">48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12</span><br><span class="line">53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7</span><br><span class="line">58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2</span><br><span class="line">63) SIGRTMAX-1  64) SIGRTMAX</span><br></pre></td></tr></table></figure>
<p>信号产生的时机和对应的默认处理动作，参考表格如下：</p>
<h3 id="查看信号信息"><a href="#查看信号信息" class="headerlink" title="查看信号信息"></a>查看信号信息</h3><p>通过 Linux 提供的 man 文档可以查询所有信号的详细信息：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看man文档的信号描述</span></span><br><span class="line">$ man 7 signal</span><br></pre></td></tr></table></figure>
<p>在信号描述中介绍了对产生的信号的五种默认处理动作，分别如下：<br>（1）Term<br>信号将进程终止<br>（2）Ign<br>信号产生之后默认被忽略<br>（3）Core<br>信号将进程终止，并且生成一个 core 文件（一般用于 gdb 调试）<br>（4）Stop<br>信号会暂停进程的运行<br>（5）Cont<br>信号会让暂停的进程继续运行</p>
<h3 id="信号的状态"><a href="#信号的状态" class="headerlink" title="信号的状态"></a>信号的状态</h3><p>Linux 中的信号有三种状态，分别为：产生、未决、递达。<br>产生：键盘输入，函数调用，执行 shell 命令，对硬件进行非法访问都会产生信号<br>未决：信号产生了，但是这个信号还没有被处理掉，这个期间信号的状态称之为未决状态<br>递达：信号被处理了（被某个进程处理掉）</p>
<h2 id="信号相关函数"><a href="#信号相关函数" class="headerlink" title="信号相关函数"></a>信号相关函数</h2><p>Linux 中能够产生信号的函数有很多，介绍几个常用函数：</p>
<h3 id="kill-raise-abort"><a href="#kill-raise-abort" class="headerlink" title="kill/raise/abort"></a>kill/raise/abort</h3><p>这三个函数的功能比较类似，可以发送相关的信号给到对应的进程。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// kill：发送指定的信号到指定的进程，函数原型如下：</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="comment">// 给某一个进程发送一个信号</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">kill</span><span class="params">(<span class="type">pid_t</span> pid, <span class="type">int</span> sig)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    pid：</span></span><br><span class="line"><span class="comment">        进程 ID</span></span><br><span class="line"><span class="comment">    sig：</span></span><br><span class="line"><span class="comment">        需要发送的信号</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// raise：给当前进程发送指定的信号，函数原型如下</span></span><br><span class="line"><span class="comment">// 给自己发送某一个信号</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">raise</span><span class="params">(<span class="type">int</span> sig)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    需要给当前进程发送的信号</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// abort：给当前进程发送一个固定信号（SIGABRT），函数原型如下：</span></span><br><span class="line"><span class="comment">// 这是一个中断函数，调用这个函数，发送一个固定信号（SIGABRT），杀死当前进程</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">abort</span><span class="params">(<span class="type">void</span>)</span></span>;</span><br></pre></td></tr></table></figure>

<h3 id="定时器"><a href="#定时器" class="headerlink" title="定时器"></a>定时器</h3><h4 id="alarm"><a href="#alarm" class="headerlink" title="alarm"></a>alarm</h4><p><code>alarm()</code> 函数只能进行单次定时，定时完成发射出一个信号。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">unsigned</span> <span class="type">int</span> <span class="title">alarm</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> seconds)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    倒计时 seconds 秒，倒计时完成发送一个信号 SIGALRM。</span></span><br><span class="line"><span class="comment">    当前进程会收到这个信号，这个信号默认的处理动作是中断当前进程</span></span><br><span class="line"><span class="comment">返回值：</span></span><br><span class="line"><span class="comment">    返回值大于 0，表示倒计时还剩多少秒</span></span><br><span class="line"><span class="comment">    返回值等于 0，表示倒计时完成，信号被发出</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<p>使用这个定时器函数<code>alarm()</code>，检测一下当前计算机 1s 钟之内能数多少个数，示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 1.设置一个定时器, 定时1s</span></span><br><span class="line">    <span class="built_in">alarm</span>(<span class="number">1</span>);  <span class="comment">// 1s之后会发出一个信号，这个信号将中断当前进程</span></span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, i++);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行示例代码的时候，计算一下时间：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 直接通过终端输出</span></span><br><span class="line">$ time ./a.out</span><br><span class="line">real    0m1.013s  <span class="comment"># 实际数数用的总时间</span></span><br><span class="line">user    0m0.060s  <span class="comment"># 用户区代码使用的时间</span></span><br><span class="line">sys     0m0.324s  <span class="comment"># 内核区使用的时间</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 不直接写终端，将数据重定向到磁盘文件中</span></span><br><span class="line">$ time ./a.out &gt; a.txt</span><br><span class="line">Alarm clock</span><br><span class="line"></span><br><span class="line">real    0m1.002s  <span class="comment"># 用户实际数数的时间变长了</span></span><br><span class="line">user    0m0.740s</span><br><span class="line">sys     0m0.236s</span><br><span class="line"></span><br><span class="line">real = user + sys + 消耗的时间（频率的从用户区到内核区进程切换）</span><br></pre></td></tr></table></figure>
<p>文件 IO 操作需要进行用户区到内核区的切换，处理方式不同，二者之间切换的频率也不同。</p>
<h4 id="setitimer"><a href="#setitimer" class="headerlink" title="setitimer"></a>setitimer</h4><p><code>setitimer()</code> 函数可以进行周期性定时，每触发一次定时器就会发射出一个信号。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这个函数可以实现周期性定时，每隔固定的一段时间，发出一个特定的定时器信号</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/time.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">itimerval</span> &#123;</span><br><span class="line">	<span class="keyword">struct</span> <span class="title class_">timeval</span> it_interval;  <span class="comment">/* 时间间隔 */</span></span><br><span class="line">	<span class="keyword">struct</span> <span class="title class_">timeval</span> it_value;     <span class="comment">/* 第一次触发定时器的时长 */</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这个结构体表示的是一个时间段: tv_sec + tv_usec</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">timeval</span> &#123;</span><br><span class="line">	<span class="type">time_t</span>      tv_sec;        <span class="comment">/* 秒 */</span></span><br><span class="line">	<span class="type">suseconds_t</span> tv_usec;       <span class="comment">/* 微妙 */</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">    举例：zhangsan 有一个闹钟，并且使用这个闹钟定时。</span></span><br><span class="line"><span class="comment">    早晨 7 点钟起床，第一次闹钟响起时可能起不来，之后每隔 5 分钟再响一次。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">    it_value: </span></span><br><span class="line"><span class="comment">        当前设置闹钟的时间点到明天早晨 7 点，对应的总秒数</span></span><br><span class="line"><span class="comment">    it_interval: </span></span><br><span class="line"><span class="comment">        闹钟第一次响过之后，每隔 5 分钟响一次</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">setitimer</span><span class="params">(<span class="type">int</span> which, <span class="type">const</span> <span class="keyword">struct</span> itimerval *new_value, </span></span></span><br><span class="line"><span class="params"><span class="function">              <span class="keyword">struct</span> itimerval *old_value)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    which: </span></span><br><span class="line"><span class="comment">        定时器使用什么样的计时法则，不同的计时法则发出的信号不同</span></span><br><span class="line"><span class="comment">        ITIMER_REAL: </span></span><br><span class="line"><span class="comment">            自然计时法，最常用。发出的信号为 SIGALRM，一般使用这个宏值。</span></span><br><span class="line"><span class="comment">            自然计时法时间 = 用户区 + 内核 + 消耗的时间（从进程的用户区到内核区切换使用的总时间）</span></span><br><span class="line"><span class="comment">        ITIMER_VIRTUAL: </span></span><br><span class="line"><span class="comment">            只计算程序在用户区运行使用的时间，发射的信号为 SIGVTALRM</span></span><br><span class="line"><span class="comment">        ITIMER_PROF: </span></span><br><span class="line"><span class="comment">            只计算内核运行使用的时间，发出的信号为 SIGPROF</span></span><br><span class="line"><span class="comment">    new_value: </span></span><br><span class="line"><span class="comment">        给定时器设置的定时信息，传入参数</span></span><br><span class="line"><span class="comment">    old_value: </span></span><br><span class="line"><span class="comment">        上一次给定时器设置的定时信息，传出参数。如果不需要这个信息，指定为 NULL</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<h2 id="信号集"><a href="#信号集" class="headerlink" title="信号集"></a>信号集</h2><h3 id="阻塞-未决信号集"><a href="#阻塞-未决信号集" class="headerlink" title="阻塞/未决信号集"></a>阻塞/未决信号集</h3><p>在 <code>PCB</code> 中有两个非常重要的信号集。一个称为 “阻塞信号集”，另一个称为 “未决信号集”。这两个信号集体现在内核中就是两张表。但是操作系统不允许我们直接对这两个信号集进行任何操作，而是需要自定义另外一个集合，借助信号集操作函数来对 <code>PCB</code> 中的这两个信号集进行修改。<br>（1）信号的“未决”是一种状态，指的是从信号的产生到信号被处理前的这一段时间。<br>（2）信号的“阻塞”是一个开关动作，指的是阻止信号被处理，但不是阻止信号产生。</p>
<p>信号的阻塞就是让系统暂时保留信号留待以后发送。一般情况下信号的阻塞只是暂时的，只是为了防止信号打断某些敏感的操作。<br><img src="/2021/08/14/%E4%BF%A1%E5%8F%B7/%E4%BF%A1%E5%8F%B7%E9%9B%86.png" alt="信号集"></p>
<p>阻塞信号集和未决信号集在内核中的结构是相同的，它们都是一个整形数组（被封装过的，一共 128 字节，int [32] == 1024 bit）。<br>1024 个标志位，其中前 31 个标志位，每一个都对应一个 Linux 中的标准信号，通过标志位的值来标记当前信号在信号集中的状态。</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 上图对信号集在内核中存储的状态的描述</span></span><br><span class="line"><span class="comment"># 前 31 个信号: 1-31，对应 1024 个标志位的前 31 个标志位</span></span><br><span class="line">信号		标志位（低地址位 -&gt; 高地址位）</span><br><span class="line">1      -&gt;  	  0</span><br><span class="line">2             1</span><br><span class="line">3             2</span><br><span class="line">4             3</span><br><span class="line">31            30	</span><br></pre></td></tr></table></figure>
<p>（1）在阻塞信号集中，描述这个信号是否被阻塞<br>默认情况下没有信号是被阻塞的，这个信号对应的标志位的值为 0。<br>如果某个信号被设置为了阻塞状态，这个信号对应的标志位被设置为 1。<br>（2）在未决信号集中，描述信号是否处于未决状态<br>如果这个信号被阻塞，不能处理，这个信号对应的标志位被设置为 1。<br>如果这个信号的阻塞被解除，未决信号集中的这个信号马上就被处理了，这个信号对应的标志位值变为 0。<br>如果这个信号没有阻塞，信号产生之后直接被处理，因此不会在未决信号集中做任何记录。</p>
<h3 id="信号集函数"><a href="#信号集函数" class="headerlink" title="信号集函数"></a>信号集函数</h3><p>因为用户是不能直接操作内核中的阻塞信号集和未决信号集的，需要调用系统函数。阻塞信号集可以通过系统函数进行读写操作，未决信号集只能对其进行读操作。</p>
<p>读/写阻塞信号集，函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="comment">// 使用这个函数修改内核中的阻塞信号集</span></span><br><span class="line"><span class="comment">// sigset_t 被封装之后得到的数据类型（原型 int[32] = 1024 个标志位），每一个信号对应一个标志位</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sigprocmask</span><span class="params">(<span class="type">int</span> how, <span class="type">const</span> <span class="type">sigset_t</span> *set, <span class="type">sigset_t</span> *oldset)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    how:</span></span><br><span class="line"><span class="comment">        SIG_BLOCK: </span></span><br><span class="line"><span class="comment">            将参数 set 集合中的信号追加到阻塞信号集中</span></span><br><span class="line"><span class="comment">        SIG_UNBLOCK: </span></span><br><span class="line"><span class="comment">            将参数 set 集合中的信号在阻塞信号集中解除阻塞</span></span><br><span class="line"><span class="comment">        SIG_SETMASK: </span></span><br><span class="line"><span class="comment">            将参数 set 集合中的数据覆盖内核的阻塞信号集数据</span></span><br><span class="line"><span class="comment">    oldset: </span></span><br><span class="line"><span class="comment">        将设置之前的阻塞信号集数据传出，如果不需要可以指定为 NULL</span></span><br><span class="line"><span class="comment">    返回值：</span></span><br><span class="line"><span class="comment">        成功，返回 0</span></span><br><span class="line"><span class="comment">        失败，返回 -1</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<p><code>sigprocmask()</code> 函数有一个 <code>sigset_t</code> 类型的参数，对这种类型的数据进行初始化需要调用一些相关的函数：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="comment">// 如果在程序中读写 sigset_t 类型的变量</span></span><br><span class="line"><span class="comment">// 阻塞信号集和未决信号集都存储在 sigset_t 类型的变量中，这个变量对应一块内存</span></span><br><span class="line"><span class="comment">// 阻塞信号集和未决信号集，对应的内存中有 1024 bit = 128 字节</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 将 set 集合中所有的标志位设置为 0</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sigemptyset</span><span class="params">(<span class="type">sigset_t</span> *set)</span></span>;</span><br><span class="line"><span class="comment">// 将 set 集合中所有的标志位设置为 1</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sigfillset</span><span class="params">(<span class="type">sigset_t</span> *set)</span></span>;</span><br><span class="line"><span class="comment">// 将 set 集合中某一个信号 signum 对应的标志位设置为 1</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sigaddset</span><span class="params">(<span class="type">sigset_t</span> *set, <span class="type">int</span> signum)</span></span>;</span><br><span class="line"><span class="comment">// 将 set 集合中某一个信号 signum 对应的标志位设置为 0</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sigdelset</span><span class="params">(<span class="type">sigset_t</span> *set, <span class="type">int</span> signum)</span></span>;</span><br><span class="line"><span class="comment">// 判断某个信号在集合中对应的标志位到底是 0 还是 1</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sigismember</span><span class="params">(<span class="type">const</span> <span class="type">sigset_t</span> *set, <span class="type">int</span> signum)</span></span>;</span><br></pre></td></tr></table></figure>
<p><img src="/2021/08/14/%E4%BF%A1%E5%8F%B7/%E4%BF%A1%E5%8F%B7%E9%9B%86%E5%87%BD%E6%95%B0.png" alt="信号集函数"></p>
<p>未决信号集不需要程序员修改。如果设置了某个信号阻塞，当这个信号产生之后，内核会将这个信号的未决状态记录到未决信号集中，当阻塞的信号被解除阻塞，未决信号集中的信号随之被处理，内核再次修改未决信号集将该信号的状态修改为递达状态（标志位置 0）。因此，写未决信号集的动作都是内核做的。</p>
<p>这是一个读未决信号集的操作函数：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="comment">// 这个函数的参数是传出参数，传出的内核未决信号集的拷贝</span></span><br><span class="line"><span class="comment">// 读一下这个集合就指定哪个信号是未决状态</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sigpending</span><span class="params">(<span class="type">sigset_t</span> *set)</span></span>;</span><br></pre></td></tr></table></figure>

<p>信号集操作函数的使用，示例代码如下：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">需求：在阻塞信号集中设置某些信号阻塞，通过一些操作产生这些信号，然后读未决信号集，最后再解除这些信号的阻塞</span><br><span class="line">假设阻塞这些信号：</span><br><span class="line">    2号信号 SIGINT: ctrl+c</span><br><span class="line">    3号信号 SIGQUIT: ctrl+\</span><br><span class="line">    9号信号 SIGKILL: shell 命令给进程发送这个信号 <span class="built_in">kill</span> -9 PID</span><br></pre></td></tr></table></figure>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 1.初始化信号集</span></span><br><span class="line">    <span class="type">sigset_t</span> myset;</span><br><span class="line">    <span class="built_in">sigemptyset</span>(&amp;myset);</span><br><span class="line">    <span class="comment">// 设置阻塞的信号</span></span><br><span class="line">    <span class="built_in">sigaddset</span>(&amp;myset, SIGINT);  <span class="comment">// 2</span></span><br><span class="line">    <span class="built_in">sigaddset</span>(&amp;myset, SIGQUIT); <span class="comment">// 3</span></span><br><span class="line">    <span class="built_in">sigaddset</span>(&amp;myset, SIGKILL); <span class="comment">// 9 测试不能被阻塞</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.将初始化的信号集中的数据设置给内核</span></span><br><span class="line">    <span class="type">sigset_t</span> old;</span><br><span class="line">    <span class="built_in">sigprocmask</span>(SIG_BLOCK, &amp;myset, &amp;old);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.让进程一直运行，在当前进程中产生对应的信号</span></span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 4.读内核的未决信号集</span></span><br><span class="line">        <span class="type">sigset_t</span> curset;</span><br><span class="line">        <span class="built_in">sigpending</span>(&amp;curset);</span><br><span class="line">        <span class="comment">// 遍历这个信号集</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt; <span class="number">32</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="type">int</span> ret = <span class="built_in">sigismember</span>(&amp;curset, i);</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;%d&quot;</span>, ret);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="number">1</span>);</span><br><span class="line">        i++;</span><br><span class="line">        <span class="keyword">if</span> (i == <span class="number">10</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 解除阻塞，重新设置阻塞信号集</span></span><br><span class="line">            <span class="comment">// sigprocmask(SIG_UNBLOCK, &amp;myset, NULL);</span></span><br><span class="line">            <span class="built_in">sigprocmask</span>(SIG_SETMASK, &amp;old, <span class="literal">NULL</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过测试得到结论：程序中对 9 号信号的阻塞是无效的，因为它无法被阻塞。<br>一张图总结这些信号集操作函数之间的关系：<br><img src="/2021/08/14/%E4%BF%A1%E5%8F%B7/%E4%BF%A1%E5%8F%B7%E9%9B%86%E6%93%8D%E4%BD%9C%E5%87%BD%E6%95%B0%E4%B9%8B%E9%97%B4%E7%9A%84%E5%85%B3%E7%B3%BB.png" alt="信号集操作函数之间的关系"></p>
<h2 id="信号捕捉"><a href="#信号捕捉" class="headerlink" title="信号捕捉"></a>信号捕捉</h2><p>Linux 中的每个信号产生之后都会有对应的默认处理行为，如果想要忽略这个信号或者修改某些信号的默认行为需要在程序中捕捉该信号。程序中进行信号捕捉是一个注册的动作，提前告诉应用程序信号产生之后做什么样的处理，当进程中对应的信号产生了，这个处理动作也就被调用了。</p>
<h3 id="signal"><a href="#signal" class="headerlink" title="signal"></a>signal</h3><p>使用 <code>signal()</code> 函数可以捕捉进程中产生的信号，并且修改捕捉到的函数的行为。这个信号的自定义处理动作是一个回调函数，内核通过 <code>signal()</code> 得到这个回调函数的地址，在信号产生之后该函数会被内核调用。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="comment">// 在程序中什么时候产生信号, 程序猿是不知道的, 因此不能在信号产生之后再去处理</span></span><br><span class="line"><span class="comment">// 在信号产生之前，提供一个注册函数，用来捕捉信号</span></span><br><span class="line"><span class="comment">// 假设在将来这个信号产生了，就委托内核进行捕捉。这个信号执行的处理动作 =&gt; 在signal函数中指定的处理动作</span></span><br><span class="line"><span class="comment">// 如果这个信号不产生, 回调函数永远不会被调用</span></span><br><span class="line"><span class="function"><span class="type">sighandler_t</span> <span class="title">signal</span><span class="params">(<span class="type">int</span> signum, <span class="type">sighandler_t</span> handler)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    signum: </span></span><br><span class="line"><span class="comment">        需要捕捉的信号</span></span><br><span class="line"><span class="comment">    handler: </span></span><br><span class="line"><span class="comment">        信号捕捉到之后的处理动作，这是一个函数指针。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 这个回调函数需要程序员写，但是程序员不调用，由内核调用。</span></span><br><span class="line"><span class="comment">// 内核调用该回调函数的时候，给它传递一个实参，这个实参的值就是捕捉的那个信号值。</span></span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*<span class="type">sighandler_t</span>)</span><span class="params">(<span class="type">int</span>)</span></span>;</span><br></pre></td></tr></table></figure>

<p>使用 <code>signal()</code> 函数来捕捉定时器产生的信号 <code>SIGALRM</code>，示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/time.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定时器信号的处理动作</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">doing</span><span class="params">(<span class="type">int</span> arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;当前捕捉到的信号是：%d\n&quot;</span>, arg);</span><br><span class="line">    <span class="comment">// 打印当前的时间</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 注册要捕捉哪一个信号，执行什么样的处理动作</span></span><br><span class="line">    <span class="built_in">signal</span>(SIGALRM, doing);</span><br><span class="line">    <span class="comment">// 1.调用定时器函数设置定时器</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">itimerval</span> newact;</span><br><span class="line">    <span class="comment">// 3s 之后发出第一个定时器信号，之后每隔 1s 发出一个定时器信号</span></span><br><span class="line">    newact.it_value.tv_sec = <span class="number">3</span>;</span><br><span class="line">    newact.it_value.tv_usec = <span class="number">0</span>;</span><br><span class="line">    newact.it_interval.tv_sec = <span class="number">1</span>;</span><br><span class="line">    newact.it_interval.tv_usec = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// 这个函数也不是阻塞函数，函数调用成功，倒计时开始</span></span><br><span class="line">    <span class="comment">// 倒计时过程中程序是继续运行的</span></span><br><span class="line">    <span class="built_in">setitimer</span>(ITIMER_REAL, &amp;newact, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 编写一个业务处理，阻止当前进程自己结束，让当前进程被发出的信号杀死</span></span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="number">1000000</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="sigaction"><a href="#sigaction" class="headerlink" title="sigaction"></a>sigaction</h3><p><code>sigaction()</code> 函数和 <code>signal()</code> 函数的功能是一样的，用于捕捉进程中产生的信号，并将用户自定义的信号行为函数（回调函数）注册给内核，内核在信号产生之后调用这个处理动作。<code>sigaction()</code> 可以看做是 <code>signal()</code> 函数是加强版，函数的参数更多、功能更强。函数原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sigaction</span><span class="params">(<span class="type">int</span> signum, <span class="type">const</span> <span class="keyword">struct</span> sigaction *act, <span class="keyword">struct</span> sigaction *oldact)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数：</span></span><br><span class="line"><span class="comment">    signum: </span></span><br><span class="line"><span class="comment">        需要捕捉的信号</span></span><br><span class="line"><span class="comment">    act: </span></span><br><span class="line"><span class="comment">        捕捉到信号之后的处理动作</span></span><br><span class="line"><span class="comment">    oldact: </span></span><br><span class="line"><span class="comment">        上一次调用该函数进行信号捕捉设置的信号处理动作，该参数一般指定为 NULL</span></span><br><span class="line"><span class="comment">    返回值：</span></span><br><span class="line"><span class="comment">        成功，返回 0</span></span><br><span class="line"><span class="comment">        失败，返回 -1</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 该函数的参数是一个结构体类型，结构体原型如下</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">sigaction</span> &#123;</span><br><span class="line">	<span class="built_in">void</span>     (*sa_handler)(<span class="type">int</span>);    <span class="comment">// 指向一个函数（回调函数）</span></span><br><span class="line">	<span class="built_in">void</span>     (*sa_sigaction)(<span class="type">int</span>, <span class="type">siginfo_t</span> *, <span class="type">void</span> *);</span><br><span class="line">	<span class="type">sigset_t</span>   sa_mask;             <span class="comment">// 初始化为空，处理函数执行期间不屏蔽任何信号</span></span><br><span class="line">	<span class="type">int</span>        sa_flags;	        <span class="comment">// 0</span></span><br><span class="line">	<span class="built_in">void</span>     (*sa_restorer)(<span class="type">void</span>);  <span class="comment">// 已废弃</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">sa_handler: </span></span><br><span class="line"><span class="comment">    函数指针，指向的函数就是捕捉到信号的处理动作</span></span><br><span class="line"><span class="comment">sa_sigaction: </span></span><br><span class="line"><span class="comment">    函数指针，指向的函数就是捕捉到信号的处理动作</span></span><br><span class="line"><span class="comment">sa_mask: </span></span><br><span class="line"><span class="comment">    在信号处理函数执行期间，临时屏蔽某些信号，将要屏蔽的信号设置到集合中。</span></span><br><span class="line"><span class="comment">    当前处理函数执行完毕，临时屏蔽自动解除。</span></span><br><span class="line"><span class="comment">    假设在这个集合中不屏蔽任何信号，默认也会屏蔽一个（捕捉的信号是谁，就临时屏蔽谁）</span></span><br><span class="line"><span class="comment">sa_flags：</span></span><br><span class="line"><span class="comment">    使用哪个函数指针指向的函数处理捕捉到的信号</span></span><br><span class="line"><span class="comment">    0：使用 sa_handler（一般情况下使用这个）</span></span><br><span class="line"><span class="comment">    SA_SIGINFO：使用 sa_sigaction（使用信号传递数据 == 进程间通信）</span></span><br><span class="line"><span class="comment">sa_restorer: </span></span><br><span class="line"><span class="comment">    被废弃的成员</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<p>通过 <code>sigaction()</code> 捕捉阻塞信号集中解除阻塞的信号，示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果捕捉多个信号，可以给不同的信号添加不同的处理动作，代码中的处理动作只有一个。</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 信号的处理动作</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">callback</span><span class="params">(<span class="type">int</span> num)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;当前捕捉的信号：%d\n&quot;</span>, num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 1.初始化信号集</span></span><br><span class="line">    <span class="type">sigset_t</span> myset;</span><br><span class="line">    <span class="built_in">sigemptyset</span>(&amp;myset);</span><br><span class="line">    <span class="comment">// 设置阻塞的信号</span></span><br><span class="line">    <span class="built_in">sigaddset</span>(&amp;myset, SIGINT);  <span class="comment">// 2</span></span><br><span class="line">    <span class="built_in">sigaddset</span>(&amp;myset, SIGQUIT); <span class="comment">// 3</span></span><br><span class="line">    <span class="built_in">sigaddset</span>(&amp;myset, SIGKILL); <span class="comment">// 9 测试不能被阻塞</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 当阻塞的信号被解除阻塞，该信号就可以被捕捉到</span></span><br><span class="line">    <span class="comment">// 如果信号被捕捉到之后，马上就被处理掉 -&gt; 递达状态</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">sigaction</span> act;</span><br><span class="line">    act.sa_handler = callback;</span><br><span class="line">    act.sa_flags = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">sigemptyset</span>(&amp;act.sa_mask);</span><br><span class="line">    <span class="built_in">sigaction</span>(SIGINT, &amp;act, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="comment">// 和 sigint 的处理动作相同</span></span><br><span class="line">    <span class="built_in">sigaction</span>(SIGQUIT, &amp;act, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">sigaction</span>(SIGKILL, &amp;act, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.将初始化的信号集中的数据设置给内核</span></span><br><span class="line">    <span class="type">sigset_t</span> old;</span><br><span class="line">    <span class="built_in">sigprocmask</span>(SIG_BLOCK, &amp;myset, &amp;old);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.让进程一直运行，在当前进程中产生对应的信号</span></span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 4.读内核的未决信号集</span></span><br><span class="line">        <span class="type">sigset_t</span> curset;</span><br><span class="line">        <span class="built_in">sigpending</span>(&amp;curset);</span><br><span class="line">        <span class="comment">// 遍历这个信号集</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt; <span class="number">32</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="type">int</span> ret = <span class="built_in">sigismember</span>(&amp;curset, i);</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;%d&quot;</span>, ret);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="number">1</span>);</span><br><span class="line">        i++;</span><br><span class="line">        <span class="keyword">if</span> (i == <span class="number">10</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 解除阻塞，重新设置阻塞信号集</span></span><br><span class="line">            <span class="comment">// sigprocmask(SIG_UNBLOCK, &amp;myset, NULL);</span></span><br><span class="line">            <span class="built_in">sigprocmask</span>(SIG_SETMASK, &amp;old, <span class="literal">NULL</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过测试得到结论：程序中对 9 号信号的捕捉是无效的，因为它无法被捕捉。</p>
<h2 id="SIGCHLD-信号"><a href="#SIGCHLD-信号" class="headerlink" title="SIGCHLD 信号"></a>SIGCHLD 信号</h2><p>当子进程退出、暂停、从暂停恢复运行的时候，在子进程中会产生一个 <code>SIGCHLD</code> 信号，并将其发送给父进程，但是父进程收到这个信号之后默认忽略了。可以在父进程中对这个信号加以利用，基于这个信号来回收子进程的资源，因此需要在父进程中捕捉子进程发送的这个信号。</p>
<p>基于信号回收子进程资源，示例代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/wait.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 回收子进程处理函数</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">recycle</span><span class="params">(<span class="type">int</span> num)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;捕捉到的信号是：%d\n&quot;</span>, num);</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">        子进程的资源回收（非阻塞）</span></span><br><span class="line"><span class="comment">        17号信号 SIGCHLD，1-31号信号不支持排队</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">        如果这些信号同时产生多个，最终处理的时候只处理一次。</span></span><br><span class="line"><span class="comment">        假设多个子进程同时退出，父进程同时收到了多个 SIGCHLD 信号。</span></span><br><span class="line"><span class="comment">        父进程只会处理一次这个信号，因此当前函数被调用了一次，waitpid 被调用一次。</span></span><br><span class="line"><span class="comment">        相当于只回收了一个子进程，但是是同时死了多个子进程，因此就出现了僵尸进程。</span></span><br><span class="line"><span class="comment">        解决方案：循环回收</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 如果是阻塞回收，就回不到另外一个处理逻辑</span></span><br><span class="line">        <span class="type">pid_t</span> pid = <span class="built_in">waitpid</span>(<span class="number">-1</span>, <span class="literal">NULL</span>, WNOHANG);</span><br><span class="line">        <span class="keyword">if</span> (pid &gt; <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;child died, pid = %d\n&quot;</span>, pid);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (pid == <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 没有死亡的子进程，直接退出当前循环</span></span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (pid == <span class="number">-1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;所有子进程都回收完毕...\n&quot;</span>);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 设置 SIGCHLD 信号阻塞</span></span><br><span class="line">    <span class="type">sigset_t</span> myset;</span><br><span class="line">    <span class="built_in">sigemptyset</span>(&amp;myset);</span><br><span class="line">    <span class="built_in">sigaddset</span>(&amp;myset, SIGCHLD);</span><br><span class="line">    <span class="built_in">sigprocmask</span>(SIG_BLOCK, &amp;myset, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 循环创建多个子进程 - 20</span></span><br><span class="line">    <span class="type">pid_t</span> pid;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">20</span>; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        pid = fork();</span><br><span class="line">        <span class="keyword">if</span> (pid == <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (pid == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;child process, pid = %d\n&quot;</span>, <span class="built_in">getpid</span>());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (pid &gt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;parent process, pid = %d\n&quot;</span>, <span class="built_in">getpid</span>());</span><br><span class="line">        <span class="comment">// 注册信号捕捉，捕捉 sigchld</span></span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">sigaction</span> act;</span><br><span class="line">        act.sa_flags  =<span class="number">0</span>;</span><br><span class="line">        act.sa_handler = recycle;</span><br><span class="line">        <span class="built_in">sigemptyset</span>(&amp;act.sa_mask);</span><br><span class="line">        <span class="comment">// 注册信号捕捉，委托内核处理将来产生的信号</span></span><br><span class="line">        <span class="comment">// 当信号产生之后，当前进程优先处理信号，之前的处理动作会暂停</span></span><br><span class="line">        <span class="comment">// 信号处理完毕之后，回到原来的暂停的位置继续运行</span></span><br><span class="line">        <span class="built_in">sigaction</span>(SIGCHLD, &amp;act, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 解除 sigcld 信号的阻塞</span></span><br><span class="line">        <span class="comment">// 信号被阻塞之后就捕捉不到了，解除阻塞之后才能捕捉到这个信号</span></span><br><span class="line">        <span class="built_in">sigprocmask</span>(SIG_UNBLOCK, &amp;myset, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 父进程执行其他业务逻辑</span></span><br><span class="line">        <span class="comment">// 默认父进程执行这个 while 循环，但是信号产生了，这个执行逻辑或强迫暂停</span></span><br><span class="line">        <span class="comment">// 父进程去处理信号的处理函数</span></span><br><span class="line">        <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">sleep</span>(<span class="number">100</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a target="_blank" rel="noopener" href="https://subingwen.cn/linux/signal">https://subingwen.cn/linux/signal</a></p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">进程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/14/%E4%BF%A1%E5%8F%B7/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
    <article id="post-守护进程" class="article article-type-post  article-index" itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2021/08/13/%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B/">守护进程</a>
    </h1>
  

        
        
<a href="/2021/08/13/%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B/" class="archive-article-date">
  	<time datetime="2021-08-13T03:48:36.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-08-13</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>守护进程（Daemon Process），是 Linux 中的后台服务进程。它是一个生存期较长的进程，通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件，一般采用以 d 结尾的名字。</p>
<h2 id="进程组"><a href="#进程组" class="headerlink" title="进程组"></a>进程组</h2><p>多个进程的集合就是进程组，这个组中必须有一个组长，组长就是进程组中的第一个进程，组长以外的都是普通的成员。<br>每个进程组都有一个唯一的组 ID，进程组的 ID 和组长的 PID 相同。</p>
<p>进程组中的成员是可以转移的，如果当前进程组中的成员被转移到了其他的组（或者所有进程都退出），那么这个进程组就不存在了。如果进程组中组长死了，但是当前进程组中有其他进程，这个进程组还是继续存在的。下面介绍几个常用的进程组函数：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 得到当前进程所在的进程组的组 ID</span></span><br><span class="line"><span class="function"><span class="type">pid_t</span> <span class="title">getpgrp</span><span class="params">(<span class="type">void</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取指定的进程所在的进程组的 ID，参数 pid 就是指定的进程</span></span><br><span class="line"><span class="function"><span class="type">pid_t</span> <span class="title">getpgid</span><span class="params">(<span class="type">pid_t</span> pid)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将某个进程移动到其他进程组中（或者创建新的进程组）</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">setpgid</span><span class="params">(<span class="type">pid_t</span> pid, <span class="type">pid_t</span> pgid)</span></span>;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">参数:</span></span><br><span class="line"><span class="comment">    pid:</span></span><br><span class="line"><span class="comment">        某个进程的进程 ID</span></span><br><span class="line"><span class="comment">    pgid: </span></span><br><span class="line"><span class="comment">        某个进程组的组 ID</span></span><br><span class="line"><span class="comment">        如果 pgid 对应的进程组存在，pid 对应的进程会移动到这个组中（pid != pgid）</span></span><br><span class="line"><span class="comment">        如果 pgid 对应的进程组不存在，会创建一个新的进程组（pid == pgid）, 当前进程是组长</span></span><br><span class="line"><span class="comment">    返回值：</span></span><br><span class="line"><span class="comment">        成功，返回 0</span></span><br><span class="line"><span class="comment">        失败，返回 -1</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<h2 id="会话"><a href="#会话" class="headerlink" title="会话"></a>会话</h2><p>会话（session）是由一个或多个进程组组成的，一个会话可以对应一个控制终端，也可以没有。一个普通的进程可以调用 <code>setsid()</code> 函数使自己成为新 session 的领头进程（会长），并且这个 session 领头进程还会被放入到一个新的进程组中。<code>setsid()</code> 函数的原型如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取某个进程所属的会话 ID</span></span><br><span class="line"><span class="function"><span class="type">pid_t</span> <span class="title">getsid</span><span class="params">(<span class="type">pid_t</span> pid)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将某个进程变成会话 =&gt; 得到一个守护进程</span></span><br><span class="line"><span class="comment">// 使用哪个进程调用这个函数，这个进程就会变成一个会话</span></span><br><span class="line"><span class="function"><span class="type">pid_t</span> <span class="title">setsid</span><span class="params">(<span class="type">void</span>)</span></span>;</span><br></pre></td></tr></table></figure>
<p>使用这个函数的注意事项：<br>（1）调用这个函数的进程不能是组长进程，如果是该函数调用失败。如何保证这个函数能调用成功？<br>先 <code>fork ()</code> 创建子进程，终止父进程，让子进程调用这个函数<br>（2）如果调用这个函数的进程不是进程组长，会话创建成功。<br>这个进程会变成当前会话中的第一个进程，同时也会变成新的进程组的组长。该函数调用成功之后，当前进程就脱离了控制终端，因此不会阻塞终端。</p>
<h2 id="创建守护进程"><a href="#创建守护进程" class="headerlink" title="创建守护进程"></a>创建守护进程</h2><p>创建一个守护进程，标准步骤如下（部分操作可以根据实际需求进行取舍）：<br>（1）创建子进程，让父进程退出<br>因为父进程有可能是组长进程，不符合条件，退出即可。<br>子进程没有任何职务，目的是让子进程最终变成一个会话，最终就会得到守护进程。<br>（2）通过子进程创建新的会话<br>调用函数 <code>setsid()</code>，脱离控制终端，变成守护进程。<br>（3）改变当前进程的工作目录（可选项）<br>某些文件系统（U 盘、移动硬盘）可以被卸载。进程如果在这些目录中运行，运行期间这些设备被卸载了，运行的进程就不能正常工作。<br>修改当前进程的工作目录需要调用函数 <code>chdir()</code><br>（4）重新设置文件的掩码（可选项）<br>掩码 umask，在创建新文件的时候需要和这个掩码进行运算，去掉文件的某些权限<br>设置掩码需要使用函数 <code>umask()</code><br>（5）关闭/重定向文件描述符（不做也可以，但是建议做）<br>启动一个进程，文件描述符表中默认有三个被打开了，对应的都是当前的终端文件。<br>因为进程通过调用 <code>setsid()</code> 已经脱离了当前终端，因此关联的文件描述符就没用了，可以关闭。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">close</span>(STDIN_FILENO);</span><br><span class="line"><span class="built_in">close</span>(STDOUT_FILENO);</span><br><span class="line"><span class="built_in">close</span>(STDERR_FILENO);</span><br></pre></td></tr></table></figure>
<p>重定向文件描述符（和关闭二选一）：改变文件描述符关联的默认文件，让它们指向一个特殊的文件 <code>/dev/null</code>，只要把数据扔到这个特殊的设备文件中，数据被销毁了。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> fd = <span class="built_in">open</span>(<span class="string">&quot;/dev/null&quot;</span>, O_RDWR);</span><br><span class="line"><span class="comment">// 重定向之后，这三个文件描述符就和当前终端没有任何关系</span></span><br><span class="line"><span class="built_in">dup2</span>(fd, STDIN_FILENO);</span><br><span class="line"><span class="built_in">dup2</span>(fd, STDOUT_FILENO);</span><br><span class="line"><span class="built_in">dup2</span>(fd, STDERR_FILENO);</span><br></pre></td></tr></table></figure>
<p>（6）根据实际需求在守护进程中执行某些特定的操作</p>
<h2 id="守护进程的应用"><a href="#守护进程的应用" class="headerlink" title="守护进程的应用"></a>守护进程的应用</h2><p>写一个守护进程，每隔 2s 获取一次系统时间，并将得到的时间写入到磁盘文件中。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/stat.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;fcntl.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/time.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;time.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 信号的处理动作</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">writeFile</span><span class="params">(<span class="type">int</span> num)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 得到系统时间</span></span><br><span class="line">    <span class="type">time_t</span> seconds = <span class="built_in">time</span>(<span class="literal">NULL</span>);</span><br><span class="line">    <span class="comment">// 时间转换（总秒数 -&gt; 可以识别的时间字符串）</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">tm</span>* loc = <span class="built_in">localtime</span>(&amp;seconds);</span><br><span class="line">    <span class="comment">// sprintf();</span></span><br><span class="line">    <span class="type">char</span>* curtime = <span class="built_in">asctime</span>(loc); <span class="comment">// 自带换行</span></span><br><span class="line">    <span class="comment">// 打开一个文件（如果文件不存在就创建，文件需要有追加属性）</span></span><br><span class="line">    <span class="comment">// ./对应的是哪个目录? /home/xxx</span></span><br><span class="line">    <span class="comment">// 0664 &amp; ~022</span></span><br><span class="line">    <span class="type">int</span> fd = <span class="built_in">open</span>(<span class="string">&quot;./time.log&quot;</span>, O_WRONLY | O_CREAT | O_APPEND, <span class="number">0664</span>);</span><br><span class="line">    <span class="built_in">write</span>(fd, curtime, <span class="built_in">strlen</span>(curtime));</span><br><span class="line">    <span class="built_in">close</span>(fd);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 1.创建子进程，杀死父进程</span></span><br><span class="line">    <span class="type">pid_t</span> pid = fork();</span><br><span class="line">    <span class="keyword">if</span> (pid &gt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 父进程</span></span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">0</span>); <span class="comment">// kill(getpid(), 9); raise(9); abort();</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.子进程（将其变成会话，脱离当前终端）</span></span><br><span class="line">    <span class="built_in">setsid</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3.修改进程的工作目录（修改到一个不能被修改和删除的目录）</span></span><br><span class="line">    <span class="built_in">chdir</span>(<span class="string">&quot;/home/xxx&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4.设置掩码，在进程中创建文件的时候这个掩码起作用</span></span><br><span class="line">    <span class="built_in">umask</span>(<span class="number">022</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 5.重定向和终端关联的文件描述符 -&gt; /dev/null</span></span><br><span class="line">    <span class="type">int</span> fd = <span class="built_in">open</span>(<span class="string">&quot;/dev/null&quot;</span>, O_RDWR);</span><br><span class="line">    <span class="built_in">dup2</span>(fd, STDIN_FILENO);</span><br><span class="line">    <span class="built_in">dup2</span>(fd, STDOUT_FILENO);</span><br><span class="line">    <span class="built_in">dup2</span>(fd, STDERR_FILENO);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 5.委托内核捕捉并处理将来发生的信号 SIGALRM(14)</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">sigaction</span> act;</span><br><span class="line">    act.sa_flags = <span class="number">0</span>;</span><br><span class="line">    act.sa_handler = writeFile;</span><br><span class="line">    <span class="built_in">sigemptyset</span>(&amp;act.sa_mask);</span><br><span class="line">    <span class="built_in">sigaction</span>(SIGALRM, &amp;act, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 6.设置定时器</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">itimerval</span> val;</span><br><span class="line">    val.it_value.tv_sec = <span class="number">2</span>;</span><br><span class="line">    val.it_value.tv_usec = <span class="number">0</span>;</span><br><span class="line">    val.it_interval.tv_sec = <span class="number">2</span>;</span><br><span class="line">    val.it_interval.tv_usec = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">setitimer</span>(ITIMER_REAL, &amp;val, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sleep</span>(<span class="number">100</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a target="_blank" rel="noopener" href="https://subingwen.cn/linux/deamon">https://subingwen.cn/linux/deamon</a></p>

      

      
    </div>
    <div class="article-info article-info-index">
      
      
	<div class="article-tag tagcloud">
		<i class="icon-price-tags icon"></i>
		<ul class="article-tag-list">
			 
        		<li class="article-tag-list-item">
        			<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">进程</a>
        		</li>
      		
		</ul>
	</div>

      

      
        <p class="article-more-link">
          <a class="article-more-a" href="/2021/08/13/%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B/">展开全文 >></a>
        </p>
      

      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
    </div>
</aside>




  
  
    <nav id="page-nav">
      <a class="extend prev" rel="prev" href="/page/2/">Prev</a><a class="page-number" href="/">1</a><a class="page-number" href="/page/2/">2</a><span class="page-number current">3</span><a class="page-number" href="/page/4/">4</a><a class="page-number" href="/page/5/">5</a><span class="space">&hellip;</span><a class="page-number" href="/page/21/">21</a><a class="extend next" rel="next" href="/page/4/">Next</a>
    </nav>
  


          </div>
        </div>
      </div>
      <footer id="footer">
  <div class="outer">
    <div id="footer-info">
    	<div class="footer-left">
    		&copy; 2022 乘风的小站
    	</div>
      	<div class="footer-right">
      		<a href="http://hexo.io/" target="_blank">Hexo</a>  Theme <a href="https://github.com/litten/hexo-theme-yilia" target="_blank">Yilia</a> by Litten
      	</div>
    </div>
  </div>
  
	<!-- 不蒜子统计 -->
	<span id="busuanzi_container_site_pv">
			本站总访问量PV：<span id="busuanzi_value_site_pv"></span>次
	</span>
	<span class="post-meta-divider">|</span>
	<span id="busuanzi_container_site_uv" style='display:none'>
			本站访客数UV：<span id="busuanzi_value_site_uv"></span>人
	</span>
	<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
  
	<!-- 添加网站运行时间 -->
	
		<span id="timeDate">载入天数...</span><span id="times">载入时分秒...</span>
		<script>
			var now = new Date();

			function createtime() {
				// var create_time = '03/29/2020 17:00:00';
				var create_time = new Date("03/29/2020 17:00:00");// 网站上线时间 
				now.setTime(now.getTime() + 250);
				days = (now - create_time) / 1000 / 60 / 60 / 24;
				dnum = Math.floor(days);
				hours = (now - create_time) / 1000 / 60 / 60 - (24 * dnum);
				hnum = Math.floor(hours);
				if (String(hnum).length == 1) {
					hnum = "0" + hnum;
				}
				minutes = (now - create_time) / 1000 / 60 - (24 * 60 * dnum) - (60 * hnum);
				mnum = Math.floor(minutes);
				if (String(mnum).length == 1) {
					mnum = "0" + mnum;
				}
				seconds = (now - create_time) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
				snum = Math.round(seconds);
				if (String(snum).length == 1) {
					snum = "0" + snum;
				}
				document.getElementById("timeDate").innerHTML = " | 本站已运行 " + dnum + " 天 ";
				document.getElementById("times").innerHTML = hnum + " 时 " + mnum + " 分 " + snum + " 秒";
			}
			setInterval("createtime()", 250);
		</script>
	
</footer>
    </div>
    <script>
	var yiliaConfig = {
		mathjax: false,
		isHome: true,
		isPost: false,
		isArchive: false,
		isTag: false,
		isCategory: false,
		open_in_new: false,
		toc_hide_index: true,
		root: "/",
		innerArchive: true,
		showTags: false
	}
</script>

<script>!function(t){function n(e){if(r[e])return r[e].exports;var i=r[e]={exports:{},id:e,loaded:!1};return t[e].call(i.exports,i,i.exports,n),i.loaded=!0,i.exports}var r={};n.m=t,n.c=r,n.p="./",n(0)}([function(t,n,r){r(195),t.exports=r(191)},function(t,n,r){var e=r(3),i=r(52),o=r(27),u=r(28),c=r(53),f="prototype",a=function(t,n,r){var s,l,h,v,p=t&a.F,d=t&a.G,y=t&a.S,g=t&a.P,b=t&a.B,m=d?e:y?e[n]||(e[n]={}):(e[n]||{})[f],x=d?i:i[n]||(i[n]={}),w=x[f]||(x[f]={});d&&(r=n);for(s in r)l=!p&&m&&void 0!==m[s],h=(l?m:r)[s],v=b&&l?c(h,e):g&&"function"==typeof h?c(Function.call,h):h,m&&u(m,s,h,t&a.U),x[s]!=h&&o(x,s,v),g&&w[s]!=h&&(w[s]=h)};e.core=i,a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},function(t,n,r){var e=r(6);t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(t,n){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(t,n){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,n,r){var e=r(126)("wks"),i=r(76),o=r(3).Symbol,u="function"==typeof o;(t.exports=function(t){return e[t]||(e[t]=u&&o[t]||(u?o:i)("Symbol."+t))}).store=e},function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},function(t,n,r){var e=r(94),i=r(33);t.exports=function(t){return e(i(t))}},function(t,n,r){t.exports=!r(4)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(2),i=r(167),o=r(50),u=Object.defineProperty;n.f=r(10)?Object.defineProperty:function(t,n,r){if(e(t),n=o(n,!0),e(r),i)try{return u(t,n,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},function(t,n,r){t.exports=!r(18)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(14),i=r(22);t.exports=r(12)?function(t,n,r){return e.f(t,n,i(1,r))}:function(t,n,r){return t[n]=r,t}},function(t,n,r){var e=r(20),i=r(58),o=r(42),u=Object.defineProperty;n.f=r(12)?Object.defineProperty:function(t,n,r){if(e(t),n=o(n,!0),e(r),i)try{return u(t,n,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},function(t,n,r){var e=r(40)("wks"),i=r(23),o=r(5).Symbol,u="function"==typeof o;(t.exports=function(t){return e[t]||(e[t]=u&&o[t]||(u?o:i)("Symbol."+t))}).store=e},function(t,n,r){var e=r(67),i=Math.min;t.exports=function(t){return t>0?i(e(t),9007199254740991):0}},function(t,n,r){var e=r(46);t.exports=function(t){return Object(e(t))}},function(t,n){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,n,r){var e=r(63),i=r(34);t.exports=Object.keys||function(t){return e(t,i)}},function(t,n,r){var e=r(21);t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},function(t,n){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},function(t,n){var r=t.exports={version:"2.4.0"};"number"==typeof __e&&(__e=r)},function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,n,r){var e=r(11),i=r(66);t.exports=r(10)?function(t,n,r){return e.f(t,n,i(1,r))}:function(t,n,r){return t[n]=r,t}},function(t,n,r){var e=r(3),i=r(27),o=r(24),u=r(76)("src"),c="toString",f=Function[c],a=(""+f).split(c);r(52).inspectSource=function(t){return f.call(t)},(t.exports=function(t,n,r,c){var f="function"==typeof r;f&&(o(r,"name")||i(r,"name",n)),t[n]!==r&&(f&&(o(r,u)||i(r,u,t[n]?""+t[n]:a.join(String(n)))),t===e?t[n]=r:c?t[n]?t[n]=r:i(t,n,r):(delete t[n],i(t,n,r)))})(Function.prototype,c,function(){return"function"==typeof this&&this[u]||f.call(this)})},function(t,n,r){var e=r(1),i=r(4),o=r(46),u=function(t,n,r,e){var i=String(o(t)),u="<"+n;return""!==r&&(u+=" "+r+'="'+String(e).replace(/"/g,"&quot;")+'"'),u+">"+i+"</"+n+">"};t.exports=function(t,n){var r={};r[t]=n(u),e(e.P+e.F*i(function(){var n=""[t]('"');return n!==n.toLowerCase()||n.split('"').length>3}),"String",r)}},function(t,n,r){var e=r(115),i=r(46);t.exports=function(t){return e(i(t))}},function(t,n,r){var e=r(116),i=r(66),o=r(30),u=r(50),c=r(24),f=r(167),a=Object.getOwnPropertyDescriptor;n.f=r(10)?a:function(t,n){if(t=o(t),n=u(n,!0),f)try{return a(t,n)}catch(t){}if(c(t,n))return i(!e.f.call(t,n),t[n])}},function(t,n,r){var e=r(24),i=r(17),o=r(145)("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),e(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on  "+t);return t}},function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,n){t.exports={}},function(t,n){t.exports=!0},function(t,n){n.f={}.propertyIsEnumerable},function(t,n,r){var e=r(14).f,i=r(8),o=r(15)("toStringTag");t.exports=function(t,n,r){t&&!i(t=r?t:t.prototype,o)&&e(t,o,{configurable:!0,value:n})}},function(t,n,r){var e=r(40)("keys"),i=r(23);t.exports=function(t){return e[t]||(e[t]=i(t))}},function(t,n,r){var e=r(5),i="__core-js_shared__",o=e[i]||(e[i]={});t.exports=function(t){return o[t]||(o[t]={})}},function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?e:r)(t)}},function(t,n,r){var e=r(21);t.exports=function(t,n){if(!e(t))return t;var r,i;if(n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;if("function"==typeof(r=t.valueOf)&&!e(i=r.call(t)))return i;if(!n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,n,r){var e=r(5),i=r(25),o=r(36),u=r(44),c=r(14).f;t.exports=function(t){var n=i.Symbol||(i.Symbol=o?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},function(t,n,r){n.f=r(15)},function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on  "+t);return t}},function(t,n,r){var e=r(4);t.exports=function(t,n){return!!t&&e(function(){n?t.call(null,function(){},1):t.call(null)})}},function(t,n,r){var e=r(53),i=r(115),o=r(17),u=r(16),c=r(203);t.exports=function(t,n){var r=1==t,f=2==t,a=3==t,s=4==t,l=6==t,h=5==t||l,v=n||c;return function(n,c,p){for(var d,y,g=o(n),b=i(g),m=e(c,p,3),x=u(b.length),w=0,S=r?v(n,x):f?v(n,0):void 0;x>w;w++)if((h||w in b)&&(d=b[w],y=m(d,w,g),t))if(r)S[w]=y;else if(y)switch(t){case 3:return!0;case 5:return d;case 6:return w;case 2:S.push(d)}else if(s)return!1;return l?-1:a||s?s:S}}},function(t,n,r){var e=r(1),i=r(52),o=r(4);t.exports=function(t,n){var r=(i.Object||{})[t]||Object[t],u={};u[t]=n(r),e(e.S+e.F*o(function(){r(1)}),"Object",u)}},function(t,n,r){var e=r(6);t.exports=function(t,n){if(!e(t))return t;var r,i;if(n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;if("function"==typeof(r=t.valueOf)&&!e(i=r.call(t)))return i;if(!n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,n,r){var e=r(5),i=r(25),o=r(91),u=r(13),c="prototype",f=function(t,n,r){var a,s,l,h=t&f.F,v=t&f.G,p=t&f.S,d=t&f.P,y=t&f.B,g=t&f.W,b=v?i:i[n]||(i[n]={}),m=b[c],x=v?e:p?e[n]:(e[n]||{})[c];v&&(r=n);for(a in r)(s=!h&&x&&void 0!==x[a])&&a in b||(l=s?x[a]:r[a],b[a]=v&&"function"!=typeof x[a]?r[a]:y&&s?o(l,e):g&&x[a]==l?function(t){var n=function(n,r,e){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(n);case 2:return new t(n,r)}return new t(n,r,e)}return t.apply(this,arguments)};return n[c]=t[c],n}(l):d&&"function"==typeof l?o(Function.call,l):l,d&&((b.virtual||(b.virtual={}))[a]=l,t&f.R&&m&&!m[a]&&u(m,a,l)))};f.F=1,f.G=2,f.S=4,f.P=8,f.B=16,f.W=32,f.U=64,f.R=128,t.exports=f},function(t,n){var r=t.exports={version:"2.4.0"};"number"==typeof __e&&(__e=r)},function(t,n,r){var e=r(26);t.exports=function(t,n,r){if(e(t),void 0===n)return t;switch(r){case 1:return function(r){return t.call(n,r)};case 2:return function(r,e){return t.call(n,r,e)};case 3:return function(r,e,i){return t.call(n,r,e,i)}}return function(){return t.apply(n,arguments)}}},function(t,n,r){var e=r(183),i=r(1),o=r(126)("metadata"),u=o.store||(o.store=new(r(186))),c=function(t,n,r){var i=u.get(t);if(!i){if(!r)return;u.set(t,i=new e)}var o=i.get(n);if(!o){if(!r)return;i.set(n,o=new e)}return o},f=function(t,n,r){var e=c(n,r,!1);return void 0!==e&&e.has(t)},a=function(t,n,r){var e=c(n,r,!1);return void 0===e?void 0:e.get(t)},s=function(t,n,r,e){c(r,e,!0).set(t,n)},l=function(t,n){var r=c(t,n,!1),e=[];return r&&r.forEach(function(t,n){e.push(n)}),e},h=function(t){return void 0===t||"symbol"==typeof t?t:String(t)},v=function(t){i(i.S,"Reflect",t)};t.exports={store:u,map:c,has:f,get:a,set:s,keys:l,key:h,exp:v}},function(t,n,r){"use strict";if(r(10)){var e=r(69),i=r(3),o=r(4),u=r(1),c=r(127),f=r(152),a=r(53),s=r(68),l=r(66),h=r(27),v=r(73),p=r(67),d=r(16),y=r(75),g=r(50),b=r(24),m=r(180),x=r(114),w=r(6),S=r(17),_=r(137),O=r(70),E=r(32),P=r(71).f,j=r(154),F=r(76),M=r(7),A=r(48),N=r(117),T=r(146),I=r(155),k=r(80),L=r(123),R=r(74),C=r(130),D=r(160),U=r(11),W=r(31),G=U.f,B=W.f,V=i.RangeError,z=i.TypeError,q=i.Uint8Array,K="ArrayBuffer",J="Shared"+K,Y="BYTES_PER_ELEMENT",H="prototype",$=Array[H],X=f.ArrayBuffer,Q=f.DataView,Z=A(0),tt=A(2),nt=A(3),rt=A(4),et=A(5),it=A(6),ot=N(!0),ut=N(!1),ct=I.values,ft=I.keys,at=I.entries,st=$.lastIndexOf,lt=$.reduce,ht=$.reduceRight,vt=$.join,pt=$.sort,dt=$.slice,yt=$.toString,gt=$.toLocaleString,bt=M("iterator"),mt=M("toStringTag"),xt=F("typed_constructor"),wt=F("def_constructor"),St=c.CONSTR,_t=c.TYPED,Ot=c.VIEW,Et="Wrong length!",Pt=A(1,function(t,n){return Tt(T(t,t[wt]),n)}),jt=o(function(){return 1===new q(new Uint16Array([1]).buffer)[0]}),Ft=!!q&&!!q[H].set&&o(function(){new q(1).set({})}),Mt=function(t,n){if(void 0===t)throw z(Et);var r=+t,e=d(t);if(n&&!m(r,e))throw V(Et);return e},At=function(t,n){var r=p(t);if(r<0||r%n)throw V("Wrong offset!");return r},Nt=function(t){if(w(t)&&_t in t)return t;throw z(t+" is not a typed array!")},Tt=function(t,n){if(!(w(t)&&xt in t))throw z("It is not a typed array constructor!");return new t(n)},It=function(t,n){return kt(T(t,t[wt]),n)},kt=function(t,n){for(var r=0,e=n.length,i=Tt(t,e);e>r;)i[r]=n[r++];return i},Lt=function(t,n,r){G(t,n,{get:function(){return this._d[r]}})},Rt=function(t){var n,r,e,i,o,u,c=S(t),f=arguments.length,s=f>1?arguments[1]:void 0,l=void 0!==s,h=j(c);if(void 0!=h&&!_(h)){for(u=h.call(c),e=[],n=0;!(o=u.next()).done;n++)e.push(o.value);c=e}for(l&&f>2&&(s=a(s,arguments[2],2)),n=0,r=d(c.length),i=Tt(this,r);r>n;n++)i[n]=l?s(c[n],n):c[n];return i},Ct=function(){for(var t=0,n=arguments.length,r=Tt(this,n);n>t;)r[t]=arguments[t++];return r},Dt=!!q&&o(function(){gt.call(new q(1))}),Ut=function(){return gt.apply(Dt?dt.call(Nt(this)):Nt(this),arguments)},Wt={copyWithin:function(t,n){return D.call(Nt(this),t,n,arguments.length>2?arguments[2]:void 0)},every:function(t){return rt(Nt(this),t,arguments.length>1?arguments[1]:void 0)},fill:function(t){return C.apply(Nt(this),arguments)},filter:function(t){return It(this,tt(Nt(this),t,arguments.length>1?arguments[1]:void 0))},find:function(t){return et(Nt(this),t,arguments.length>1?arguments[1]:void 0)},findIndex:function(t){return it(Nt(this),t,arguments.length>1?arguments[1]:void 0)},forEach:function(t){Z(Nt(this),t,arguments.length>1?arguments[1]:void 0)},indexOf:function(t){return ut(Nt(this),t,arguments.length>1?arguments[1]:void 0)},includes:function(t){return ot(Nt(this),t,arguments.length>1?arguments[1]:void 0)},join:function(t){return vt.apply(Nt(this),arguments)},lastIndexOf:function(t){return st.apply(Nt(this),arguments)},map:function(t){return Pt(Nt(this),t,arguments.length>1?arguments[1]:void 0)},reduce:function(t){return lt.apply(Nt(this),arguments)},reduceRight:function(t){return ht.apply(Nt(this),arguments)},reverse:function(){for(var t,n=this,r=Nt(n).length,e=Math.floor(r/2),i=0;i<e;)t=n[i],n[i++]=n[--r],n[r]=t;return n},some:function(t){return nt(Nt(this),t,arguments.length>1?arguments[1]:void 0)},sort:function(t){return pt.call(Nt(this),t)},subarray:function(t,n){var r=Nt(this),e=r.length,i=y(t,e);return new(T(r,r[wt]))(r.buffer,r.byteOffset+i*r.BYTES_PER_ELEMENT,d((void 0===n?e:y(n,e))-i))}},Gt=function(t,n){return It(this,dt.call(Nt(this),t,n))},Bt=function(t){Nt(this);var n=At(arguments[1],1),r=this.length,e=S(t),i=d(e.length),o=0;if(i+n>r)throw V(Et);for(;o<i;)this[n+o]=e[o++]},Vt={entries:function(){return at.call(Nt(this))},keys:function(){return ft.call(Nt(this))},values:function(){return ct.call(Nt(this))}},zt=function(t,n){return w(t)&&t[_t]&&"symbol"!=typeof n&&n in t&&String(+n)==String(n)},qt=function(t,n){return zt(t,n=g(n,!0))?l(2,t[n]):B(t,n)},Kt=function(t,n,r){return!(zt(t,n=g(n,!0))&&w(r)&&b(r,"value"))||b(r,"get")||b(r,"set")||r.configurable||b(r,"writable")&&!r.writable||b(r,"enumerable")&&!r.enumerable?G(t,n,r):(t[n]=r.value,t)};St||(W.f=qt,U.f=Kt),u(u.S+u.F*!St,"Object",{getOwnPropertyDescriptor:qt,defineProperty:Kt}),o(function(){yt.call({})})&&(yt=gt=function(){return vt.call(this)});var Jt=v({},Wt);v(Jt,Vt),h(Jt,bt,Vt.values),v(Jt,{slice:Gt,set:Bt,constructor:function(){},toString:yt,toLocaleString:Ut}),Lt(Jt,"buffer","b"),Lt(Jt,"byteOffset","o"),Lt(Jt,"byteLength","l"),Lt(Jt,"length","e"),G(Jt,mt,{get:function(){return this[_t]}}),t.exports=function(t,n,r,f){f=!!f;var a=t+(f?"Clamped":"")+"Array",l="Uint8Array"!=a,v="get"+t,p="set"+t,y=i[a],g=y||{},b=y&&E(y),m=!y||!c.ABV,S={},_=y&&y[H],j=function(t,r){var e=t._d;return e.v[v](r*n+e.o,jt)},F=function(t,r,e){var i=t._d;f&&(e=(e=Math.round(e))<0?0:e>255?255:255&e),i.v[p](r*n+i.o,e,jt)},M=function(t,n){G(t,n,{get:function(){return j(this,n)},set:function(t){return F(this,n,t)},enumerable:!0})};m?(y=r(function(t,r,e,i){s(t,y,a,"_d");var o,u,c,f,l=0,v=0;if(w(r)){if(!(r instanceof X||(f=x(r))==K||f==J))return _t in r?kt(y,r):Rt.call(y,r);o=r,v=At(e,n);var p=r.byteLength;if(void 0===i){if(p%n)throw V(Et);if((u=p-v)<0)throw V(Et)}else if((u=d(i)*n)+v>p)throw V(Et);c=u/n}else c=Mt(r,!0),u=c*n,o=new X(u);for(h(t,"_d",{b:o,o:v,l:u,e:c,v:new Q(o)});l<c;)M(t,l++)}),_=y[H]=O(Jt),h(_,"constructor",y)):L(function(t){new y(null),new y(t)},!0)||(y=r(function(t,r,e,i){s(t,y,a);var o;return w(r)?r instanceof X||(o=x(r))==K||o==J?void 0!==i?new g(r,At(e,n),i):void 0!==e?new g(r,At(e,n)):new g(r):_t in r?kt(y,r):Rt.call(y,r):new g(Mt(r,l))}),Z(b!==Function.prototype?P(g).concat(P(b)):P(g),function(t){t in y||h(y,t,g[t])}),y[H]=_,e||(_.constructor=y));var A=_[bt],N=!!A&&("values"==A.name||void 0==A.name),T=Vt.values;h(y,xt,!0),h(_,_t,a),h(_,Ot,!0),h(_,wt,y),(f?new y(1)[mt]==a:mt in _)||G(_,mt,{get:function(){return a}}),S[a]=y,u(u.G+u.W+u.F*(y!=g),S),u(u.S,a,{BYTES_PER_ELEMENT:n,from:Rt,of:Ct}),Y in _||h(_,Y,n),u(u.P,a,Wt),R(a),u(u.P+u.F*Ft,a,{set:Bt}),u(u.P+u.F*!N,a,Vt),u(u.P+u.F*(_.toString!=yt),a,{toString:yt}),u(u.P+u.F*o(function(){new y(1).slice()}),a,{slice:Gt}),u(u.P+u.F*(o(function(){return[1,2].toLocaleString()!=new y([1,2]).toLocaleString()})||!o(function(){_.toLocaleString.call([1,2])})),a,{toLocaleString:Ut}),k[a]=N?A:T,e||N||h(_,bt,T)}}else t.exports=function(){}},function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,n,r){var e=r(21),i=r(5).document,o=e(i)&&e(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,n,r){t.exports=!r(12)&&!r(18)(function(){return 7!=Object.defineProperty(r(57)("div"),"a",{get:function(){return 7}}).a})},function(t,n,r){"use strict";var e=r(36),i=r(51),o=r(64),u=r(13),c=r(8),f=r(35),a=r(96),s=r(38),l=r(103),h=r(15)("iterator"),v=!([].keys&&"next"in[].keys()),p="keys",d="values",y=function(){return this};t.exports=function(t,n,r,g,b,m,x){a(r,n,g);var w,S,_,O=function(t){if(!v&&t in F)return F[t];switch(t){case p:case d:return function(){return new r(this,t)}}return function(){return new r(this,t)}},E=n+" Iterator",P=b==d,j=!1,F=t.prototype,M=F[h]||F["@@iterator"]||b&&F[b],A=M||O(b),N=b?P?O("entries"):A:void 0,T="Array"==n?F.entries||M:M;if(T&&(_=l(T.call(new t)))!==Object.prototype&&(s(_,E,!0),e||c(_,h)||u(_,h,y)),P&&M&&M.name!==d&&(j=!0,A=function(){return M.call(this)}),e&&!x||!v&&!j&&F[h]||u(F,h,A),f[n]=A,f[E]=y,b)if(w={values:P?A:O(d),keys:m?A:O(p),entries:N},x)for(S in w)S in F||o(F,S,w[S]);else i(i.P+i.F*(v||j),n,w);return w}},function(t,n,r){var e=r(20),i=r(100),o=r(34),u=r(39)("IE_PROTO"),c=function(){},f="prototype",a=function(){var t,n=r(57)("iframe"),e=o.length;for(n.style.display="none",r(93).appendChild(n),n.src="javascript:",t=n.contentWindow.document,t.open(),t.write("<script>document.F=Object<\/script>"),t.close(),a=t.F;e--;)delete a[f][o[e]];return a()};t.exports=Object.create||function(t,n){var r;return null!==t?(c[f]=e(t),r=new c,c[f]=null,r[u]=t):r=a(),void 0===n?r:i(r,n)}},function(t,n,r){var e=r(63),i=r(34).concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,i)}},function(t,n){n.f=Object.getOwnPropertySymbols},function(t,n,r){var e=r(8),i=r(9),o=r(90)(!1),u=r(39)("IE_PROTO");t.exports=function(t,n){var r,c=i(t),f=0,a=[];for(r in c)r!=u&&e(c,r)&&a.push(r);for(;n.length>f;)e(c,r=n[f++])&&(~o(a,r)||a.push(r));return a}},function(t,n,r){t.exports=r(13)},function(t,n,r){var e=r(76)("meta"),i=r(6),o=r(24),u=r(11).f,c=0,f=Object.isExtensible||function(){return!0},a=!r(4)(function(){return f(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=function(t,n){if(!i(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!o(t,e)){if(!f(t))return"F";if(!n)return"E";s(t)}return t[e].i},h=function(t,n){if(!o(t,e)){if(!f(t))return!0;if(!n)return!1;s(t)}return t[e].w},v=function(t){return a&&p.NEED&&f(t)&&!o(t,e)&&s(t),t},p=t.exports={KEY:e,NEED:!1,fastKey:l,getWeak:h,onFreeze:v}},function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?e:r)(t)}},function(t,n){t.exports=function(t,n,r,e){if(!(t instanceof n)||void 0!==e&&e in t)throw TypeError(r+": incorrect invocation!");return t}},function(t,n){t.exports=!1},function(t,n,r){var e=r(2),i=r(173),o=r(133),u=r(145)("IE_PROTO"),c=function(){},f="prototype",a=function(){var t,n=r(132)("iframe"),e=o.length;for(n.style.display="none",r(135).appendChild(n),n.src="javascript:",t=n.contentWindow.document,t.open(),t.write("<script>document.F=Object<\/script>"),t.close(),a=t.F;e--;)delete a[f][o[e]];return a()};t.exports=Object.create||function(t,n){var r;return null!==t?(c[f]=e(t),r=new c,c[f]=null,r[u]=t):r=a(),void 0===n?r:i(r,n)}},function(t,n,r){var e=r(175),i=r(133).concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,i)}},function(t,n,r){var e=r(175),i=r(133);t.exports=Object.keys||function(t){return e(t,i)}},function(t,n,r){var e=r(28);t.exports=function(t,n,r){for(var i in n)e(t,i,n[i],r);return t}},function(t,n,r){"use strict";var e=r(3),i=r(11),o=r(10),u=r(7)("species");t.exports=function(t){var n=e[t];o&&n&&!n[u]&&i.f(n,u,{configurable:!0,get:function(){return this}})}},function(t,n,r){var e=r(67),i=Math.max,o=Math.min;t.exports=function(t,n){return t=e(t),t<0?i(t+n,0):o(t,n)}},function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},function(t,n,r){var e=r(33);t.exports=function(t){return Object(e(t))}},function(t,n,r){var e=r(7)("unscopables"),i=Array.prototype;void 0==i[e]&&r(27)(i,e,{}),t.exports=function(t){i[e][t]=!0}},function(t,n,r){var e=r(53),i=r(169),o=r(137),u=r(2),c=r(16),f=r(154),a={},s={},n=t.exports=function(t,n,r,l,h){var v,p,d,y,g=h?function(){return t}:f(t),b=e(r,l,n?2:1),m=0;if("function"!=typeof g)throw TypeError(t+" is not iterable!");if(o(g)){for(v=c(t.length);v>m;m++)if((y=n?b(u(p=t[m])[0],p[1]):b(t[m]))===a||y===s)return y}else for(d=g.call(t);!(p=d.next()).done;)if((y=i(d,b,p.value,n))===a||y===s)return y};n.BREAK=a,n.RETURN=s},function(t,n){t.exports={}},function(t,n,r){var e=r(11).f,i=r(24),o=r(7)("toStringTag");t.exports=function(t,n,r){t&&!i(t=r?t:t.prototype,o)&&e(t,o,{configurable:!0,value:n})}},function(t,n,r){var e=r(1),i=r(46),o=r(4),u=r(150),c="["+u+"]",f="​",a=RegExp("^"+c+c+"*"),s=RegExp(c+c+"*$"),l=function(t,n,r){var i={},c=o(function(){return!!u[t]()||f[t]()!=f}),a=i[t]=c?n(h):u[t];r&&(i[r]=a),e(e.P+e.F*c,"String",i)},h=l.trim=function(t,n){return t=String(i(t)),1&n&&(t=t.replace(a,"")),2&n&&(t=t.replace(s,"")),t};t.exports=l},function(t,n,r){t.exports={default:r(86),__esModule:!0}},function(t,n,r){t.exports={default:r(87),__esModule:!0}},function(t,n,r){"use strict";function e(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0;var i=r(84),o=e(i),u=r(83),c=e(u),f="function"==typeof c.default&&"symbol"==typeof o.default?function(t){return typeof t}:function(t){return t&&"function"==typeof c.default&&t.constructor===c.default&&t!==c.default.prototype?"symbol":typeof t};n.default="function"==typeof c.default&&"symbol"===f(o.default)?function(t){return void 0===t?"undefined":f(t)}:function(t){return t&&"function"==typeof c.default&&t.constructor===c.default&&t!==c.default.prototype?"symbol":void 0===t?"undefined":f(t)}},function(t,n,r){r(110),r(108),r(111),r(112),t.exports=r(25).Symbol},function(t,n,r){r(109),r(113),t.exports=r(44).f("iterator")},function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,n){t.exports=function(){}},function(t,n,r){var e=r(9),i=r(106),o=r(105);t.exports=function(t){return function(n,r,u){var c,f=e(n),a=i(f.length),s=o(u,a);if(t&&r!=r){for(;a>s;)if((c=f[s++])!=c)return!0}else for(;a>s;s++)if((t||s in f)&&f[s]===r)return t||s||0;return!t&&-1}}},function(t,n,r){var e=r(88);t.exports=function(t,n,r){if(e(t),void 0===n)return t;switch(r){case 1:return function(r){return t.call(n,r)};case 2:return function(r,e){return t.call(n,r,e)};case 3:return function(r,e,i){return t.call(n,r,e,i)}}return function(){return t.apply(n,arguments)}}},function(t,n,r){var e=r(19),i=r(62),o=r(37);t.exports=function(t){var n=e(t),r=i.f;if(r)for(var u,c=r(t),f=o.f,a=0;c.length>a;)f.call(t,u=c[a++])&&n.push(u);return n}},function(t,n,r){t.exports=r(5).document&&document.documentElement},function(t,n,r){var e=r(56);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},function(t,n,r){var e=r(56);t.exports=Array.isArray||function(t){return"Array"==e(t)}},function(t,n,r){"use strict";var e=r(60),i=r(22),o=r(38),u={};r(13)(u,r(15)("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:i(1,r)}),o(t,n+" Iterator")}},function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},function(t,n,r){var e=r(19),i=r(9);t.exports=function(t,n){for(var r,o=i(t),u=e(o),c=u.length,f=0;c>f;)if(o[r=u[f++]]===n)return r}},function(t,n,r){var e=r(23)("meta"),i=r(21),o=r(8),u=r(14).f,c=0,f=Object.isExtensible||function(){return!0},a=!r(18)(function(){return f(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=function(t,n){if(!i(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!o(t,e)){if(!f(t))return"F";if(!n)return"E";s(t)}return t[e].i},h=function(t,n){if(!o(t,e)){if(!f(t))return!0;if(!n)return!1;s(t)}return t[e].w},v=function(t){return a&&p.NEED&&f(t)&&!o(t,e)&&s(t),t},p=t.exports={KEY:e,NEED:!1,fastKey:l,getWeak:h,onFreeze:v}},function(t,n,r){var e=r(14),i=r(20),o=r(19);t.exports=r(12)?Object.defineProperties:function(t,n){i(t);for(var r,u=o(n),c=u.length,f=0;c>f;)e.f(t,r=u[f++],n[r]);return t}},function(t,n,r){var e=r(37),i=r(22),o=r(9),u=r(42),c=r(8),f=r(58),a=Object.getOwnPropertyDescriptor;n.f=r(12)?a:function(t,n){if(t=o(t),n=u(n,!0),f)try{return a(t,n)}catch(t){}if(c(t,n))return i(!e.f.call(t,n),t[n])}},function(t,n,r){var e=r(9),i=r(61).f,o={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],c=function(t){try{return i(t)}catch(t){return u.slice()}};t.exports.f=function(t){return u&&"[object Window]"==o.call(t)?c(t):i(e(t))}},function(t,n,r){var e=r(8),i=r(77),o=r(39)("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),e(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,n,r){var e=r(41),i=r(33);t.exports=function(t){return function(n,r){var o,u,c=String(i(n)),f=e(r),a=c.length;return f<0||f>=a?t?"":void 0:(o=c.charCodeAt(f),o<55296||o>56319||f+1===a||(u=c.charCodeAt(f+1))<56320||u>57343?t?c.charAt(f):o:t?c.slice(f,f+2):u-56320+(o-55296<<10)+65536)}}},function(t,n,r){var e=r(41),i=Math.max,o=Math.min;t.exports=function(t,n){return t=e(t),t<0?i(t+n,0):o(t,n)}},function(t,n,r){var e=r(41),i=Math.min;t.exports=function(t){return t>0?i(e(t),9007199254740991):0}},function(t,n,r){"use strict";var e=r(89),i=r(97),o=r(35),u=r(9);t.exports=r(59)(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,i(1)):"keys"==n?i(0,r):"values"==n?i(0,t[r]):i(0,[r,t[r]])},"values"),o.Arguments=o.Array,e("keys"),e("values"),e("entries")},function(t,n){},function(t,n,r){"use strict";var e=r(104)(!0);r(59)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},function(t,n,r){"use strict";var e=r(5),i=r(8),o=r(12),u=r(51),c=r(64),f=r(99).KEY,a=r(18),s=r(40),l=r(38),h=r(23),v=r(15),p=r(44),d=r(43),y=r(98),g=r(92),b=r(95),m=r(20),x=r(9),w=r(42),S=r(22),_=r(60),O=r(102),E=r(101),P=r(14),j=r(19),F=E.f,M=P.f,A=O.f,N=e.Symbol,T=e.JSON,I=T&&T.stringify,k="prototype",L=v("_hidden"),R=v("toPrimitive"),C={}.propertyIsEnumerable,D=s("symbol-registry"),U=s("symbols"),W=s("op-symbols"),G=Object[k],B="function"==typeof N,V=e.QObject,z=!V||!V[k]||!V[k].findChild,q=o&&a(function(){return 7!=_(M({},"a",{get:function(){return M(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=F(G,n);e&&delete G[n],M(t,n,r),e&&t!==G&&M(G,n,e)}:M,K=function(t){var n=U[t]=_(N[k]);return n._k=t,n},J=B&&"symbol"==typeof N.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof N},Y=function(t,n,r){return t===G&&Y(W,n,r),m(t),n=w(n,!0),m(r),i(U,n)?(r.enumerable?(i(t,L)&&t[L][n]&&(t[L][n]=!1),r=_(r,{enumerable:S(0,!1)})):(i(t,L)||M(t,L,S(1,{})),t[L][n]=!0),q(t,n,r)):M(t,n,r)},H=function(t,n){m(t);for(var r,e=g(n=x(n)),i=0,o=e.length;o>i;)Y(t,r=e[i++],n[r]);return t},$=function(t,n){return void 0===n?_(t):H(_(t),n)},X=function(t){var n=C.call(this,t=w(t,!0));return!(this===G&&i(U,t)&&!i(W,t))&&(!(n||!i(this,t)||!i(U,t)||i(this,L)&&this[L][t])||n)},Q=function(t,n){if(t=x(t),n=w(n,!0),t!==G||!i(U,n)||i(W,n)){var r=F(t,n);return!r||!i(U,n)||i(t,L)&&t[L][n]||(r.enumerable=!0),r}},Z=function(t){for(var n,r=A(x(t)),e=[],o=0;r.length>o;)i(U,n=r[o++])||n==L||n==f||e.push(n);return e},tt=function(t){for(var n,r=t===G,e=A(r?W:x(t)),o=[],u=0;e.length>u;)!i(U,n=e[u++])||r&&!i(G,n)||o.push(U[n]);return o};B||(N=function(){if(this instanceof N)throw TypeError("Symbol is not a constructor!");var t=h(arguments.length>0?arguments[0]:void 0),n=function(r){this===G&&n.call(W,r),i(this,L)&&i(this[L],t)&&(this[L][t]=!1),q(this,t,S(1,r))};return o&&z&&q(G,t,{configurable:!0,set:n}),K(t)},c(N[k],"toString",function(){return this._k}),E.f=Q,P.f=Y,r(61).f=O.f=Z,r(37).f=X,r(62).f=tt,o&&!r(36)&&c(G,"propertyIsEnumerable",X,!0),p.f=function(t){return K(v(t))}),u(u.G+u.W+u.F*!B,{Symbol:N});for(var nt="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),rt=0;nt.length>rt;)v(nt[rt++]);for(var nt=j(v.store),rt=0;nt.length>rt;)d(nt[rt++]);u(u.S+u.F*!B,"Symbol",{for:function(t){return i(D,t+="")?D[t]:D[t]=N(t)},keyFor:function(t){if(J(t))return y(D,t);throw TypeError(t+" is not a symbol!")},useSetter:function(){z=!0},useSimple:function(){z=!1}}),u(u.S+u.F*!B,"Object",{create:$,defineProperty:Y,defineProperties:H,getOwnPropertyDescriptor:Q,getOwnPropertyNames:Z,getOwnPropertySymbols:tt}),T&&u(u.S+u.F*(!B||a(function(){var t=N();return"[null]"!=I([t])||"{}"!=I({a:t})||"{}"!=I(Object(t))})),"JSON",{stringify:function(t){if(void 0!==t&&!J(t)){for(var n,r,e=[t],i=1;arguments.length>i;)e.push(arguments[i++]);return n=e[1],"function"==typeof n&&(r=n),!r&&b(n)||(n=function(t,n){if(r&&(n=r.call(this,t,n)),!J(n))return n}),e[1]=n,I.apply(T,e)}}}),N[k][R]||r(13)(N[k],R,N[k].valueOf),l(N,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(t,n,r){r(43)("asyncIterator")},function(t,n,r){r(43)("observable")},function(t,n,r){r(107);for(var e=r(5),i=r(13),o=r(35),u=r(15)("toStringTag"),c=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],f=0;f<5;f++){var a=c[f],s=e[a],l=s&&s.prototype;l&&!l[u]&&i(l,u,a),o[a]=o.Array}},function(t,n,r){var e=r(45),i=r(7)("toStringTag"),o="Arguments"==e(function(){return arguments}()),u=function(t,n){try{return t[n]}catch(t){}};t.exports=function(t){var n,r,c;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(r=u(n=Object(t),i))?r:o?e(n):"Object"==(c=e(n))&&"function"==typeof n.callee?"Arguments":c}},function(t,n,r){var e=r(45);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},function(t,n){n.f={}.propertyIsEnumerable},function(t,n,r){var e=r(30),i=r(16),o=r(75);t.exports=function(t){return function(n,r,u){var c,f=e(n),a=i(f.length),s=o(u,a);if(t&&r!=r){for(;a>s;)if((c=f[s++])!=c)return!0}else for(;a>s;s++)if((t||s in f)&&f[s]===r)return t||s||0;return!t&&-1}}},function(t,n,r){"use strict";var e=r(3),i=r(1),o=r(28),u=r(73),c=r(65),f=r(79),a=r(68),s=r(6),l=r(4),h=r(123),v=r(81),p=r(136);t.exports=function(t,n,r,d,y,g){var b=e[t],m=b,x=y?"set":"add",w=m&&m.prototype,S={},_=function(t){var n=w[t];o(w,t,"delete"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"has"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"get"==t?function(t){return g&&!s(t)?void 0:n.call(this,0===t?0:t)}:"add"==t?function(t){return n.call(this,0===t?0:t),this}:function(t,r){return n.call(this,0===t?0:t,r),this})};if("function"==typeof m&&(g||w.forEach&&!l(function(){(new m).entries().next()}))){var O=new m,E=O[x](g?{}:-0,1)!=O,P=l(function(){O.has(1)}),j=h(function(t){new m(t)}),F=!g&&l(function(){for(var t=new m,n=5;n--;)t[x](n,n);return!t.has(-0)});j||(m=n(function(n,r){a(n,m,t);var e=p(new b,n,m);return void 0!=r&&f(r,y,e[x],e),e}),m.prototype=w,w.constructor=m),(P||F)&&(_("delete"),_("has"),y&&_("get")),(F||E)&&_(x),g&&w.clear&&delete w.clear}else m=d.getConstructor(n,t,y,x),u(m.prototype,r),c.NEED=!0;return v(m,t),S[t]=m,i(i.G+i.W+i.F*(m!=b),S),g||d.setStrong(m,t,y),m}},function(t,n,r){"use strict";var e=r(27),i=r(28),o=r(4),u=r(46),c=r(7);t.exports=function(t,n,r){var f=c(t),a=r(u,f,""[t]),s=a[0],l=a[1];o(function(){var n={};return n[f]=function(){return 7},7!=""[t](n)})&&(i(String.prototype,t,s),e(RegExp.prototype,f,2==n?function(t,n){return l.call(t,this,n)}:function(t){return l.call(t,this)}))}
},function(t,n,r){"use strict";var e=r(2);t.exports=function(){var t=e(this),n="";return t.global&&(n+="g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}},function(t,n){t.exports=function(t,n,r){var e=void 0===r;switch(n.length){case 0:return e?t():t.call(r);case 1:return e?t(n[0]):t.call(r,n[0]);case 2:return e?t(n[0],n[1]):t.call(r,n[0],n[1]);case 3:return e?t(n[0],n[1],n[2]):t.call(r,n[0],n[1],n[2]);case 4:return e?t(n[0],n[1],n[2],n[3]):t.call(r,n[0],n[1],n[2],n[3])}return t.apply(r,n)}},function(t,n,r){var e=r(6),i=r(45),o=r(7)("match");t.exports=function(t){var n;return e(t)&&(void 0!==(n=t[o])?!!n:"RegExp"==i(t))}},function(t,n,r){var e=r(7)("iterator"),i=!1;try{var o=[7][e]();o.return=function(){i=!0},Array.from(o,function(){throw 2})}catch(t){}t.exports=function(t,n){if(!n&&!i)return!1;var r=!1;try{var o=[7],u=o[e]();u.next=function(){return{done:r=!0}},o[e]=function(){return u},t(o)}catch(t){}return r}},function(t,n,r){t.exports=r(69)||!r(4)(function(){var t=Math.random();__defineSetter__.call(null,t,function(){}),delete r(3)[t]})},function(t,n){n.f=Object.getOwnPropertySymbols},function(t,n,r){var e=r(3),i="__core-js_shared__",o=e[i]||(e[i]={});t.exports=function(t){return o[t]||(o[t]={})}},function(t,n,r){for(var e,i=r(3),o=r(27),u=r(76),c=u("typed_array"),f=u("view"),a=!(!i.ArrayBuffer||!i.DataView),s=a,l=0,h="Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array".split(",");l<9;)(e=i[h[l++]])?(o(e.prototype,c,!0),o(e.prototype,f,!0)):s=!1;t.exports={ABV:a,CONSTR:s,TYPED:c,VIEW:f}},function(t,n){"use strict";var r={versions:function(){var t=window.navigator.userAgent;return{trident:t.indexOf("Trident")>-1,presto:t.indexOf("Presto")>-1,webKit:t.indexOf("AppleWebKit")>-1,gecko:t.indexOf("Gecko")>-1&&-1==t.indexOf("KHTML"),mobile:!!t.match(/AppleWebKit.*Mobile.*/),ios:!!t.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),android:t.indexOf("Android")>-1||t.indexOf("Linux")>-1,iPhone:t.indexOf("iPhone")>-1||t.indexOf("Mac")>-1,iPad:t.indexOf("iPad")>-1,webApp:-1==t.indexOf("Safari"),weixin:-1==t.indexOf("MicroMessenger")}}()};t.exports=r},function(t,n,r){"use strict";var e=r(85),i=function(t){return t&&t.__esModule?t:{default:t}}(e),o=function(){function t(t,n,e){return n||e?String.fromCharCode(n||e):r[t]||t}function n(t){return e[t]}var r={"&quot;":'"',"&lt;":"<","&gt;":">","&amp;":"&","&nbsp;":" "},e={};for(var u in r)e[r[u]]=u;return r["&apos;"]="'",e["'"]="&#39;",{encode:function(t){return t?(""+t).replace(/['<> "&]/g,n).replace(/\r?\n/g,"<br/>").replace(/\s/g,"&nbsp;"):""},decode:function(n){return n?(""+n).replace(/<br\s*\/?>/gi,"\n").replace(/&quot;|&lt;|&gt;|&amp;|&nbsp;|&apos;|&#(\d+);|&#(\d+)/g,t).replace(/\u00a0/g," "):""},encodeBase16:function(t){if(!t)return t;t+="";for(var n=[],r=0,e=t.length;e>r;r++)n.push(t.charCodeAt(r).toString(16).toUpperCase());return n.join("")},encodeBase16forJSON:function(t){if(!t)return t;t=t.replace(/[\u4E00-\u9FBF]/gi,function(t){return escape(t).replace("%u","\\u")});for(var n=[],r=0,e=t.length;e>r;r++)n.push(t.charCodeAt(r).toString(16).toUpperCase());return n.join("")},decodeBase16:function(t){if(!t)return t;t+="";for(var n=[],r=0,e=t.length;e>r;r+=2)n.push(String.fromCharCode("0x"+t.slice(r,r+2)));return n.join("")},encodeObject:function(t){if(t instanceof Array)for(var n=0,r=t.length;r>n;n++)t[n]=o.encodeObject(t[n]);else if("object"==(void 0===t?"undefined":(0,i.default)(t)))for(var e in t)t[e]=o.encodeObject(t[e]);else if("string"==typeof t)return o.encode(t);return t},loadScript:function(t){var n=document.createElement("script");document.getElementsByTagName("body")[0].appendChild(n),n.setAttribute("src",t)},addLoadEvent:function(t){var n=window.onload;"function"!=typeof window.onload?window.onload=t:window.onload=function(){n(),t()}}}}();t.exports=o},function(t,n,r){"use strict";var e=r(17),i=r(75),o=r(16);t.exports=function(t){for(var n=e(this),r=o(n.length),u=arguments.length,c=i(u>1?arguments[1]:void 0,r),f=u>2?arguments[2]:void 0,a=void 0===f?r:i(f,r);a>c;)n[c++]=t;return n}},function(t,n,r){"use strict";var e=r(11),i=r(66);t.exports=function(t,n,r){n in t?e.f(t,n,i(0,r)):t[n]=r}},function(t,n,r){var e=r(6),i=r(3).document,o=e(i)&&e(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,n,r){var e=r(7)("match");t.exports=function(t){var n=/./;try{"/./"[t](n)}catch(r){try{return n[e]=!1,!"/./"[t](n)}catch(t){}}return!0}},function(t,n,r){t.exports=r(3).document&&document.documentElement},function(t,n,r){var e=r(6),i=r(144).set;t.exports=function(t,n,r){var o,u=n.constructor;return u!==r&&"function"==typeof u&&(o=u.prototype)!==r.prototype&&e(o)&&i&&i(t,o),t}},function(t,n,r){var e=r(80),i=r(7)("iterator"),o=Array.prototype;t.exports=function(t){return void 0!==t&&(e.Array===t||o[i]===t)}},function(t,n,r){var e=r(45);t.exports=Array.isArray||function(t){return"Array"==e(t)}},function(t,n,r){"use strict";var e=r(70),i=r(66),o=r(81),u={};r(27)(u,r(7)("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:i(1,r)}),o(t,n+" Iterator")}},function(t,n,r){"use strict";var e=r(69),i=r(1),o=r(28),u=r(27),c=r(24),f=r(80),a=r(139),s=r(81),l=r(32),h=r(7)("iterator"),v=!([].keys&&"next"in[].keys()),p="keys",d="values",y=function(){return this};t.exports=function(t,n,r,g,b,m,x){a(r,n,g);var w,S,_,O=function(t){if(!v&&t in F)return F[t];switch(t){case p:case d:return function(){return new r(this,t)}}return function(){return new r(this,t)}},E=n+" Iterator",P=b==d,j=!1,F=t.prototype,M=F[h]||F["@@iterator"]||b&&F[b],A=M||O(b),N=b?P?O("entries"):A:void 0,T="Array"==n?F.entries||M:M;if(T&&(_=l(T.call(new t)))!==Object.prototype&&(s(_,E,!0),e||c(_,h)||u(_,h,y)),P&&M&&M.name!==d&&(j=!0,A=function(){return M.call(this)}),e&&!x||!v&&!j&&F[h]||u(F,h,A),f[n]=A,f[E]=y,b)if(w={values:P?A:O(d),keys:m?A:O(p),entries:N},x)for(S in w)S in F||o(F,S,w[S]);else i(i.P+i.F*(v||j),n,w);return w}},function(t,n){var r=Math.expm1;t.exports=!r||r(10)>22025.465794806718||r(10)<22025.465794806718||-2e-17!=r(-2e-17)?function(t){return 0==(t=+t)?t:t>-1e-6&&t<1e-6?t+t*t/2:Math.exp(t)-1}:r},function(t,n){t.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:t<0?-1:1}},function(t,n,r){var e=r(3),i=r(151).set,o=e.MutationObserver||e.WebKitMutationObserver,u=e.process,c=e.Promise,f="process"==r(45)(u);t.exports=function(){var t,n,r,a=function(){var e,i;for(f&&(e=u.domain)&&e.exit();t;){i=t.fn,t=t.next;try{i()}catch(e){throw t?r():n=void 0,e}}n=void 0,e&&e.enter()};if(f)r=function(){u.nextTick(a)};else if(o){var s=!0,l=document.createTextNode("");new o(a).observe(l,{characterData:!0}),r=function(){l.data=s=!s}}else if(c&&c.resolve){var h=c.resolve();r=function(){h.then(a)}}else r=function(){i.call(e,a)};return function(e){var i={fn:e,next:void 0};n&&(n.next=i),t||(t=i,r()),n=i}}},function(t,n,r){var e=r(6),i=r(2),o=function(t,n){if(i(t),!e(n)&&null!==n)throw TypeError(n+": can't set as prototype!")};t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,n,e){try{e=r(53)(Function.call,r(31).f(Object.prototype,"__proto__").set,2),e(t,[]),n=!(t instanceof Array)}catch(t){n=!0}return function(t,r){return o(t,r),n?t.__proto__=r:e(t,r),t}}({},!1):void 0),check:o}},function(t,n,r){var e=r(126)("keys"),i=r(76);t.exports=function(t){return e[t]||(e[t]=i(t))}},function(t,n,r){var e=r(2),i=r(26),o=r(7)("species");t.exports=function(t,n){var r,u=e(t).constructor;return void 0===u||void 0==(r=e(u)[o])?n:i(r)}},function(t,n,r){var e=r(67),i=r(46);t.exports=function(t){return function(n,r){var o,u,c=String(i(n)),f=e(r),a=c.length;return f<0||f>=a?t?"":void 0:(o=c.charCodeAt(f),o<55296||o>56319||f+1===a||(u=c.charCodeAt(f+1))<56320||u>57343?t?c.charAt(f):o:t?c.slice(f,f+2):u-56320+(o-55296<<10)+65536)}}},function(t,n,r){var e=r(122),i=r(46);t.exports=function(t,n,r){if(e(n))throw TypeError("String#"+r+" doesn't accept regex!");return String(i(t))}},function(t,n,r){"use strict";var e=r(67),i=r(46);t.exports=function(t){var n=String(i(this)),r="",o=e(t);if(o<0||o==1/0)throw RangeError("Count can't be negative");for(;o>0;(o>>>=1)&&(n+=n))1&o&&(r+=n);return r}},function(t,n){t.exports="\t\n\v\f\r   ᠎             　\u2028\u2029\ufeff"},function(t,n,r){var e,i,o,u=r(53),c=r(121),f=r(135),a=r(132),s=r(3),l=s.process,h=s.setImmediate,v=s.clearImmediate,p=s.MessageChannel,d=0,y={},g="onreadystatechange",b=function(){var t=+this;if(y.hasOwnProperty(t)){var n=y[t];delete y[t],n()}},m=function(t){b.call(t.data)};h&&v||(h=function(t){for(var n=[],r=1;arguments.length>r;)n.push(arguments[r++]);return y[++d]=function(){c("function"==typeof t?t:Function(t),n)},e(d),d},v=function(t){delete y[t]},"process"==r(45)(l)?e=function(t){l.nextTick(u(b,t,1))}:p?(i=new p,o=i.port2,i.port1.onmessage=m,e=u(o.postMessage,o,1)):s.addEventListener&&"function"==typeof postMessage&&!s.importScripts?(e=function(t){s.postMessage(t+"","*")},s.addEventListener("message",m,!1)):e=g in a("script")?function(t){f.appendChild(a("script"))[g]=function(){f.removeChild(this),b.call(t)}}:function(t){setTimeout(u(b,t,1),0)}),t.exports={set:h,clear:v}},function(t,n,r){"use strict";var e=r(3),i=r(10),o=r(69),u=r(127),c=r(27),f=r(73),a=r(4),s=r(68),l=r(67),h=r(16),v=r(71).f,p=r(11).f,d=r(130),y=r(81),g="ArrayBuffer",b="DataView",m="prototype",x="Wrong length!",w="Wrong index!",S=e[g],_=e[b],O=e.Math,E=e.RangeError,P=e.Infinity,j=S,F=O.abs,M=O.pow,A=O.floor,N=O.log,T=O.LN2,I="buffer",k="byteLength",L="byteOffset",R=i?"_b":I,C=i?"_l":k,D=i?"_o":L,U=function(t,n,r){var e,i,o,u=Array(r),c=8*r-n-1,f=(1<<c)-1,a=f>>1,s=23===n?M(2,-24)-M(2,-77):0,l=0,h=t<0||0===t&&1/t<0?1:0;for(t=F(t),t!=t||t===P?(i=t!=t?1:0,e=f):(e=A(N(t)/T),t*(o=M(2,-e))<1&&(e--,o*=2),t+=e+a>=1?s/o:s*M(2,1-a),t*o>=2&&(e++,o/=2),e+a>=f?(i=0,e=f):e+a>=1?(i=(t*o-1)*M(2,n),e+=a):(i=t*M(2,a-1)*M(2,n),e=0));n>=8;u[l++]=255&i,i/=256,n-=8);for(e=e<<n|i,c+=n;c>0;u[l++]=255&e,e/=256,c-=8);return u[--l]|=128*h,u},W=function(t,n,r){var e,i=8*r-n-1,o=(1<<i)-1,u=o>>1,c=i-7,f=r-1,a=t[f--],s=127&a;for(a>>=7;c>0;s=256*s+t[f],f--,c-=8);for(e=s&(1<<-c)-1,s>>=-c,c+=n;c>0;e=256*e+t[f],f--,c-=8);if(0===s)s=1-u;else{if(s===o)return e?NaN:a?-P:P;e+=M(2,n),s-=u}return(a?-1:1)*e*M(2,s-n)},G=function(t){return t[3]<<24|t[2]<<16|t[1]<<8|t[0]},B=function(t){return[255&t]},V=function(t){return[255&t,t>>8&255]},z=function(t){return[255&t,t>>8&255,t>>16&255,t>>24&255]},q=function(t){return U(t,52,8)},K=function(t){return U(t,23,4)},J=function(t,n,r){p(t[m],n,{get:function(){return this[r]}})},Y=function(t,n,r,e){var i=+r,o=l(i);if(i!=o||o<0||o+n>t[C])throw E(w);var u=t[R]._b,c=o+t[D],f=u.slice(c,c+n);return e?f:f.reverse()},H=function(t,n,r,e,i,o){var u=+r,c=l(u);if(u!=c||c<0||c+n>t[C])throw E(w);for(var f=t[R]._b,a=c+t[D],s=e(+i),h=0;h<n;h++)f[a+h]=s[o?h:n-h-1]},$=function(t,n){s(t,S,g);var r=+n,e=h(r);if(r!=e)throw E(x);return e};if(u.ABV){if(!a(function(){new S})||!a(function(){new S(.5)})){S=function(t){return new j($(this,t))};for(var X,Q=S[m]=j[m],Z=v(j),tt=0;Z.length>tt;)(X=Z[tt++])in S||c(S,X,j[X]);o||(Q.constructor=S)}var nt=new _(new S(2)),rt=_[m].setInt8;nt.setInt8(0,2147483648),nt.setInt8(1,2147483649),!nt.getInt8(0)&&nt.getInt8(1)||f(_[m],{setInt8:function(t,n){rt.call(this,t,n<<24>>24)},setUint8:function(t,n){rt.call(this,t,n<<24>>24)}},!0)}else S=function(t){var n=$(this,t);this._b=d.call(Array(n),0),this[C]=n},_=function(t,n,r){s(this,_,b),s(t,S,b);var e=t[C],i=l(n);if(i<0||i>e)throw E("Wrong offset!");if(r=void 0===r?e-i:h(r),i+r>e)throw E(x);this[R]=t,this[D]=i,this[C]=r},i&&(J(S,k,"_l"),J(_,I,"_b"),J(_,k,"_l"),J(_,L,"_o")),f(_[m],{getInt8:function(t){return Y(this,1,t)[0]<<24>>24},getUint8:function(t){return Y(this,1,t)[0]},getInt16:function(t){var n=Y(this,2,t,arguments[1]);return(n[1]<<8|n[0])<<16>>16},getUint16:function(t){var n=Y(this,2,t,arguments[1]);return n[1]<<8|n[0]},getInt32:function(t){return G(Y(this,4,t,arguments[1]))},getUint32:function(t){return G(Y(this,4,t,arguments[1]))>>>0},getFloat32:function(t){return W(Y(this,4,t,arguments[1]),23,4)},getFloat64:function(t){return W(Y(this,8,t,arguments[1]),52,8)},setInt8:function(t,n){H(this,1,t,B,n)},setUint8:function(t,n){H(this,1,t,B,n)},setInt16:function(t,n){H(this,2,t,V,n,arguments[2])},setUint16:function(t,n){H(this,2,t,V,n,arguments[2])},setInt32:function(t,n){H(this,4,t,z,n,arguments[2])},setUint32:function(t,n){H(this,4,t,z,n,arguments[2])},setFloat32:function(t,n){H(this,4,t,K,n,arguments[2])},setFloat64:function(t,n){H(this,8,t,q,n,arguments[2])}});y(S,g),y(_,b),c(_[m],u.VIEW,!0),n[g]=S,n[b]=_},function(t,n,r){var e=r(3),i=r(52),o=r(69),u=r(182),c=r(11).f;t.exports=function(t){var n=i.Symbol||(i.Symbol=o?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},function(t,n,r){var e=r(114),i=r(7)("iterator"),o=r(80);t.exports=r(52).getIteratorMethod=function(t){if(void 0!=t)return t[i]||t["@@iterator"]||o[e(t)]}},function(t,n,r){"use strict";var e=r(78),i=r(170),o=r(80),u=r(30);t.exports=r(140)(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,i(1)):"keys"==n?i(0,r):"values"==n?i(0,t[r]):i(0,[r,t[r]])},"values"),o.Arguments=o.Array,e("keys"),e("values"),e("entries")},function(t,n){function r(t,n){t.classList?t.classList.add(n):t.className+=" "+n}t.exports=r},function(t,n){function r(t,n){if(t.classList)t.classList.remove(n);else{var r=new RegExp("(^|\\b)"+n.split(" ").join("|")+"(\\b|$)","gi");t.className=t.className.replace(r," ")}}t.exports=r},function(t,n){function r(){throw new Error("setTimeout has not been defined")}function e(){throw new Error("clearTimeout has not been defined")}function i(t){if(s===setTimeout)return setTimeout(t,0);if((s===r||!s)&&setTimeout)return s=setTimeout,setTimeout(t,0);try{return s(t,0)}catch(n){try{return s.call(null,t,0)}catch(n){return s.call(this,t,0)}}}function o(t){if(l===clearTimeout)return clearTimeout(t);if((l===e||!l)&&clearTimeout)return l=clearTimeout,clearTimeout(t);try{return l(t)}catch(n){try{return l.call(null,t)}catch(n){return l.call(this,t)}}}function u(){d&&v&&(d=!1,v.length?p=v.concat(p):y=-1,p.length&&c())}function c(){if(!d){var t=i(u);d=!0;for(var n=p.length;n;){for(v=p,p=[];++y<n;)v&&v[y].run();y=-1,n=p.length}v=null,d=!1,o(t)}}function f(t,n){this.fun=t,this.array=n}function a(){}var s,l,h=t.exports={};!function(){try{s="function"==typeof setTimeout?setTimeout:r}catch(t){s=r}try{l="function"==typeof clearTimeout?clearTimeout:e}catch(t){l=e}}();var v,p=[],d=!1,y=-1;h.nextTick=function(t){var n=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r<arguments.length;r++)n[r-1]=arguments[r];p.push(new f(t,n)),1!==p.length||d||i(c)},f.prototype.run=function(){this.fun.apply(null,this.array)},h.title="browser",h.browser=!0,h.env={},h.argv=[],h.version="",h.versions={},h.on=a,h.addListener=a,h.once=a,h.off=a,h.removeListener=a,h.removeAllListeners=a,h.emit=a,h.prependListener=a,h.prependOnceListener=a,h.listeners=function(t){return[]},h.binding=function(t){throw new Error("process.binding is not supported")},h.cwd=function(){return"/"},h.chdir=function(t){throw new Error("process.chdir is not supported")},h.umask=function(){return 0}},function(t,n,r){var e=r(45);t.exports=function(t,n){if("number"!=typeof t&&"Number"!=e(t))throw TypeError(n);return+t}},function(t,n,r){"use strict";var e=r(17),i=r(75),o=r(16);t.exports=[].copyWithin||function(t,n){var r=e(this),u=o(r.length),c=i(t,u),f=i(n,u),a=arguments.length>2?arguments[2]:void 0,s=Math.min((void 0===a?u:i(a,u))-f,u-c),l=1;for(f<c&&c<f+s&&(l=-1,f+=s-1,c+=s-1);s-- >0;)f in r?r[c]=r[f]:delete r[c],c+=l,f+=l;return r}},function(t,n,r){var e=r(79);t.exports=function(t,n){var r=[];return e(t,!1,r.push,r,n),r}},function(t,n,r){var e=r(26),i=r(17),o=r(115),u=r(16);t.exports=function(t,n,r,c,f){e(n);var a=i(t),s=o(a),l=u(a.length),h=f?l-1:0,v=f?-1:1;if(r<2)for(;;){if(h in s){c=s[h],h+=v;break}if(h+=v,f?h<0:l<=h)throw TypeError("Reduce of empty array with no initial value")}for(;f?h>=0:l>h;h+=v)h in s&&(c=n(c,s[h],h,a));return c}},function(t,n,r){"use strict";var e=r(26),i=r(6),o=r(121),u=[].slice,c={},f=function(t,n,r){if(!(n in c)){for(var e=[],i=0;i<n;i++)e[i]="a["+i+"]";c[n]=Function("F,a","return new F("+e.join(",")+")")}return c[n](t,r)};t.exports=Function.bind||function(t){var n=e(this),r=u.call(arguments,1),c=function(){var e=r.concat(u.call(arguments));return this instanceof c?f(n,e.length,e):o(n,e,t)};return i(n.prototype)&&(c.prototype=n.prototype),c}},function(t,n,r){"use strict";var e=r(11).f,i=r(70),o=r(73),u=r(53),c=r(68),f=r(46),a=r(79),s=r(140),l=r(170),h=r(74),v=r(10),p=r(65).fastKey,d=v?"_s":"size",y=function(t,n){var r,e=p(n);if("F"!==e)return t._i[e];for(r=t._f;r;r=r.n)if(r.k==n)return r};t.exports={getConstructor:function(t,n,r,s){var l=t(function(t,e){c(t,l,n,"_i"),t._i=i(null),t._f=void 0,t._l=void 0,t[d]=0,void 0!=e&&a(e,r,t[s],t)});return o(l.prototype,{clear:function(){for(var t=this,n=t._i,r=t._f;r;r=r.n)r.r=!0,r.p&&(r.p=r.p.n=void 0),delete n[r.i];t._f=t._l=void 0,t[d]=0},delete:function(t){var n=this,r=y(n,t);if(r){var e=r.n,i=r.p;delete n._i[r.i],r.r=!0,i&&(i.n=e),e&&(e.p=i),n._f==r&&(n._f=e),n._l==r&&(n._l=i),n[d]--}return!!r},forEach:function(t){c(this,l,"forEach");for(var n,r=u(t,arguments.length>1?arguments[1]:void 0,3);n=n?n.n:this._f;)for(r(n.v,n.k,this);n&&n.r;)n=n.p},has:function(t){return!!y(this,t)}}),v&&e(l.prototype,"size",{get:function(){return f(this[d])}}),l},def:function(t,n,r){var e,i,o=y(t,n);return o?o.v=r:(t._l=o={i:i=p(n,!0),k:n,v:r,p:e=t._l,n:void 0,r:!1},t._f||(t._f=o),e&&(e.n=o),t[d]++,"F"!==i&&(t._i[i]=o)),t},getEntry:y,setStrong:function(t,n,r){s(t,n,function(t,n){this._t=t,this._k=n,this._l=void 0},function(){for(var t=this,n=t._k,r=t._l;r&&r.r;)r=r.p;return t._t&&(t._l=r=r?r.n:t._t._f)?"keys"==n?l(0,r.k):"values"==n?l(0,r.v):l(0,[r.k,r.v]):(t._t=void 0,l(1))},r?"entries":"values",!r,!0),h(n)}}},function(t,n,r){var e=r(114),i=r(161);t.exports=function(t){return function(){if(e(this)!=t)throw TypeError(t+"#toJSON isn't generic");return i(this)}}},function(t,n,r){"use strict";var e=r(73),i=r(65).getWeak,o=r(2),u=r(6),c=r(68),f=r(79),a=r(48),s=r(24),l=a(5),h=a(6),v=0,p=function(t){return t._l||(t._l=new d)},d=function(){this.a=[]},y=function(t,n){return l(t.a,function(t){return t[0]===n})};d.prototype={get:function(t){var n=y(this,t);if(n)return n[1]},has:function(t){return!!y(this,t)},set:function(t,n){var r=y(this,t);r?r[1]=n:this.a.push([t,n])},delete:function(t){var n=h(this.a,function(n){return n[0]===t});return~n&&this.a.splice(n,1),!!~n}},t.exports={getConstructor:function(t,n,r,o){var a=t(function(t,e){c(t,a,n,"_i"),t._i=v++,t._l=void 0,void 0!=e&&f(e,r,t[o],t)});return e(a.prototype,{delete:function(t){if(!u(t))return!1;var n=i(t);return!0===n?p(this).delete(t):n&&s(n,this._i)&&delete n[this._i]},has:function(t){if(!u(t))return!1;var n=i(t);return!0===n?p(this).has(t):n&&s(n,this._i)}}),a},def:function(t,n,r){var e=i(o(n),!0);return!0===e?p(t).set(n,r):e[t._i]=r,t},ufstore:p}},function(t,n,r){t.exports=!r(10)&&!r(4)(function(){return 7!=Object.defineProperty(r(132)("div"),"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(6),i=Math.floor;t.exports=function(t){return!e(t)&&isFinite(t)&&i(t)===t}},function(t,n,r){var e=r(2);t.exports=function(t,n,r,i){try{return i?n(e(r)[0],r[1]):n(r)}catch(n){var o=t.return;throw void 0!==o&&e(o.call(t)),n}}},function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},function(t,n){t.exports=Math.log1p||function(t){return(t=+t)>-1e-8&&t<1e-8?t-t*t/2:Math.log(1+t)}},function(t,n,r){"use strict";var e=r(72),i=r(125),o=r(116),u=r(17),c=r(115),f=Object.assign;t.exports=!f||r(4)(function(){var t={},n={},r=Symbol(),e="abcdefghijklmnopqrst";return t[r]=7,e.split("").forEach(function(t){n[t]=t}),7!=f({},t)[r]||Object.keys(f({},n)).join("")!=e})?function(t,n){for(var r=u(t),f=arguments.length,a=1,s=i.f,l=o.f;f>a;)for(var h,v=c(arguments[a++]),p=s?e(v).concat(s(v)):e(v),d=p.length,y=0;d>y;)l.call(v,h=p[y++])&&(r[h]=v[h]);return r}:f},function(t,n,r){var e=r(11),i=r(2),o=r(72);t.exports=r(10)?Object.defineProperties:function(t,n){i(t);for(var r,u=o(n),c=u.length,f=0;c>f;)e.f(t,r=u[f++],n[r]);return t}},function(t,n,r){var e=r(30),i=r(71).f,o={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],c=function(t){try{return i(t)}catch(t){return u.slice()}};t.exports.f=function(t){return u&&"[object Window]"==o.call(t)?c(t):i(e(t))}},function(t,n,r){var e=r(24),i=r(30),o=r(117)(!1),u=r(145)("IE_PROTO");t.exports=function(t,n){var r,c=i(t),f=0,a=[];for(r in c)r!=u&&e(c,r)&&a.push(r);for(;n.length>f;)e(c,r=n[f++])&&(~o(a,r)||a.push(r));return a}},function(t,n,r){var e=r(72),i=r(30),o=r(116).f;t.exports=function(t){return function(n){for(var r,u=i(n),c=e(u),f=c.length,a=0,s=[];f>a;)o.call(u,r=c[a++])&&s.push(t?[r,u[r]]:u[r]);return s}}},function(t,n,r){var e=r(71),i=r(125),o=r(2),u=r(3).Reflect;t.exports=u&&u.ownKeys||function(t){var n=e.f(o(t)),r=i.f;return r?n.concat(r(t)):n}},function(t,n,r){var e=r(3).parseFloat,i=r(82).trim;t.exports=1/e(r(150)+"-0")!=-1/0?function(t){var n=i(String(t),3),r=e(n);return 0===r&&"-"==n.charAt(0)?-0:r}:e},function(t,n,r){var e=r(3).parseInt,i=r(82).trim,o=r(150),u=/^[\-+]?0[xX]/;t.exports=8!==e(o+"08")||22!==e(o+"0x16")?function(t,n){var r=i(String(t),3);return e(r,n>>>0||(u.test(r)?16:10))}:e},function(t,n){t.exports=Object.is||function(t,n){return t===n?0!==t||1/t==1/n:t!=t&&n!=n}},function(t,n,r){var e=r(16),i=r(149),o=r(46);t.exports=function(t,n,r,u){var c=String(o(t)),f=c.length,a=void 0===r?" ":String(r),s=e(n);if(s<=f||""==a)return c;var l=s-f,h=i.call(a,Math.ceil(l/a.length));return h.length>l&&(h=h.slice(0,l)),u?h+c:c+h}},function(t,n,r){n.f=r(7)},function(t,n,r){"use strict";var e=r(164);t.exports=r(118)("Map",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{get:function(t){var n=e.getEntry(this,t);return n&&n.v},set:function(t,n){return e.def(this,0===t?0:t,n)}},e,!0)},function(t,n,r){r(10)&&"g"!=/./g.flags&&r(11).f(RegExp.prototype,"flags",{configurable:!0,get:r(120)})},function(t,n,r){"use strict";var e=r(164);t.exports=r(118)("Set",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{add:function(t){return e.def(this,t=0===t?0:t,t)}},e)},function(t,n,r){"use strict";var e,i=r(48)(0),o=r(28),u=r(65),c=r(172),f=r(166),a=r(6),s=u.getWeak,l=Object.isExtensible,h=f.ufstore,v={},p=function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},d={get:function(t){if(a(t)){var n=s(t);return!0===n?h(this).get(t):n?n[this._i]:void 0}},set:function(t,n){return f.def(this,t,n)}},y=t.exports=r(118)("WeakMap",p,d,f,!0,!0);7!=(new y).set((Object.freeze||Object)(v),7).get(v)&&(e=f.getConstructor(p),c(e.prototype,d),u.NEED=!0,i(["delete","has","get","set"],function(t){var n=y.prototype,r=n[t];o(n,t,function(n,i){if(a(n)&&!l(n)){this._f||(this._f=new e);var o=this._f[t](n,i);return"set"==t?this:o}return r.call(this,n,i)})}))},,,,function(t,n){"use strict";function r(){var t=document.querySelector("#page-nav");if(t&&!document.querySelector("#page-nav .extend.prev")&&(t.innerHTML='<a class="extend prev disabled" rel="prev">Prev</a>'+t.innerHTML),t&&!document.querySelector("#page-nav .extend.next")&&(t.innerHTML=t.innerHTML+'<a class="extend next disabled" rel="next">Next</a>'),yiliaConfig&&yiliaConfig.open_in_new){document.querySelectorAll(".article-entry a:not(.article-more-a)").forEach(function(t){var n=t.getAttribute("target");n&&""!==n||t.setAttribute("target","_blank")})}if(yiliaConfig&&yiliaConfig.toc_hide_index){document.querySelectorAll(".toc-number").forEach(function(t){t.style.display="none"})}var n=document.querySelector("#js-aboutme");n&&0!==n.length&&(n.innerHTML=n.innerText)}t.exports={init:r}},function(t,n,r){"use strict";function e(t){return t&&t.__esModule?t:{default:t}}function i(t,n){var r=/\/|index.html/g;return t.replace(r,"")===n.replace(r,"")}function o(){for(var t=document.querySelectorAll(".js-header-menu li a"),n=window.location.pathname,r=0,e=t.length;r<e;r++){var o=t[r];i(n,o.getAttribute("href"))&&(0,h.default)(o,"active")}}function u(t){for(var n=t.offsetLeft,r=t.offsetParent;null!==r;)n+=r.offsetLeft,r=r.offsetParent;return n}function c(t){for(var n=t.offsetTop,r=t.offsetParent;null!==r;)n+=r.offsetTop,r=r.offsetParent;return n}function f(t,n,r,e,i){var o=u(t),f=c(t)-n;if(f-r<=i){var a=t.$newDom;a||(a=t.cloneNode(!0),(0,d.default)(t,a),t.$newDom=a,a.style.position="fixed",a.style.top=(r||f)+"px",a.style.left=o+"px",a.style.zIndex=e||2,a.style.width="100%",a.style.color="#fff"),a.style.visibility="visible",t.style.visibility="hidden"}else{t.style.visibility="visible";var s=t.$newDom;s&&(s.style.visibility="hidden")}}function a(){var t=document.querySelector(".js-overlay"),n=document.querySelector(".js-header-menu");f(t,document.body.scrollTop,-63,2,0),f(n,document.body.scrollTop,1,3,0)}function s(){document.querySelector("#container").addEventListener("scroll",function(t){a()}),window.addEventListener("scroll",function(t){a()}),a()}var l=r(156),h=e(l),v=r(157),p=(e(v),r(382)),d=e(p),y=r(128),g=e(y),b=r(190),m=e(b),x=r(129);(function(){g.default.versions.mobile&&window.screen.width<800&&(o(),s())})(),(0,x.addLoadEvent)(function(){m.default.init()}),t.exports={}},,,,function(t,n,r){(function(t){"use strict";function n(t,n,r){t[n]||Object[e](t,n,{writable:!0,configurable:!0,value:r})}if(r(381),r(391),r(198),t._babelPolyfill)throw new Error("only one instance of babel-polyfill is allowed");t._babelPolyfill=!0;var e="defineProperty";n(String.prototype,"padLeft","".padStart),n(String.prototype,"padRight","".padEnd),"pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,find,findIndex,includes,join,slice,concat,push,splice,unshift,sort,lastIndexOf,reduce,reduceRight,copyWithin,fill".split(",").forEach(function(t){[][t]&&n(Array,t,Function.call.bind([][t]))})}).call(n,function(){return this}())},,,function(t,n,r){r(210),t.exports=r(52).RegExp.escape},,,,function(t,n,r){var e=r(6),i=r(138),o=r(7)("species");t.exports=function(t){var n;return i(t)&&(n=t.constructor,"function"!=typeof n||n!==Array&&!i(n.prototype)||(n=void 0),e(n)&&null===(n=n[o])&&(n=void 0)),void 0===n?Array:n}},function(t,n,r){var e=r(202);t.exports=function(t,n){return new(e(t))(n)}},function(t,n,r){"use strict";var e=r(2),i=r(50),o="number";t.exports=function(t){if("string"!==t&&t!==o&&"default"!==t)throw TypeError("Incorrect hint");return i(e(this),t!=o)}},function(t,n,r){var e=r(72),i=r(125),o=r(116);t.exports=function(t){var n=e(t),r=i.f;if(r)for(var u,c=r(t),f=o.f,a=0;c.length>a;)f.call(t,u=c[a++])&&n.push(u);return n}},function(t,n,r){var e=r(72),i=r(30);t.exports=function(t,n){for(var r,o=i(t),u=e(o),c=u.length,f=0;c>f;)if(o[r=u[f++]]===n)return r}},function(t,n,r){"use strict";var e=r(208),i=r(121),o=r(26);t.exports=function(){for(var t=o(this),n=arguments.length,r=Array(n),u=0,c=e._,f=!1;n>u;)(r[u]=arguments[u++])===c&&(f=!0);return function(){var e,o=this,u=arguments.length,a=0,s=0;if(!f&&!u)return i(t,r,o);if(e=r.slice(),f)for(;n>a;a++)e[a]===c&&(e[a]=arguments[s++]);for(;u>s;)e.push(arguments[s++]);return i(t,e,o)}}},function(t,n,r){t.exports=r(3)},function(t,n){t.exports=function(t,n){var r=n===Object(n)?function(t){return n[t]}:n;return function(n){return String(n).replace(t,r)}}},function(t,n,r){var e=r(1),i=r(209)(/[\\^$*+?.()|[\]{}]/g,"\\$&");e(e.S,"RegExp",{escape:function(t){return i(t)}})},function(t,n,r){var e=r(1);e(e.P,"Array",{copyWithin:r(160)}),r(78)("copyWithin")},function(t,n,r){"use strict";var e=r(1),i=r(48)(4);e(e.P+e.F*!r(47)([].every,!0),"Array",{every:function(t){return i(this,t,arguments[1])}})},function(t,n,r){var e=r(1);e(e.P,"Array",{fill:r(130)}),r(78)("fill")},function(t,n,r){"use strict";var e=r(1),i=r(48)(2);e(e.P+e.F*!r(47)([].filter,!0),"Array",{filter:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(48)(6),o="findIndex",u=!0;o in[]&&Array(1)[o](function(){u=!1}),e(e.P+e.F*u,"Array",{findIndex:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r(78)(o)},function(t,n,r){"use strict";var e=r(1),i=r(48)(5),o="find",u=!0;o in[]&&Array(1)[o](function(){u=!1}),e(e.P+e.F*u,"Array",{find:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r(78)(o)},function(t,n,r){"use strict";var e=r(1),i=r(48)(0),o=r(47)([].forEach,!0);e(e.P+e.F*!o,"Array",{forEach:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(53),i=r(1),o=r(17),u=r(169),c=r(137),f=r(16),a=r(131),s=r(154);i(i.S+i.F*!r(123)(function(t){Array.from(t)}),"Array",{from:function(t){var n,r,i,l,h=o(t),v="function"==typeof this?this:Array,p=arguments.length,d=p>1?arguments[1]:void 0,y=void 0!==d,g=0,b=s(h);if(y&&(d=e(d,p>2?arguments[2]:void 0,2)),void 0==b||v==Array&&c(b))for(n=f(h.length),r=new v(n);n>g;g++)a(r,g,y?d(h[g],g):h[g]);else for(l=b.call(h),r=new v;!(i=l.next()).done;g++)a(r,g,y?u(l,d,[i.value,g],!0):i.value);return r.length=g,r}})},function(t,n,r){"use strict";var e=r(1),i=r(117)(!1),o=[].indexOf,u=!!o&&1/[1].indexOf(1,-0)<0;e(e.P+e.F*(u||!r(47)(o)),"Array",{indexOf:function(t){return u?o.apply(this,arguments)||0:i(this,t,arguments[1])}})},function(t,n,r){var e=r(1);e(e.S,"Array",{isArray:r(138)})},function(t,n,r){"use strict";var e=r(1),i=r(30),o=[].join;e(e.P+e.F*(r(115)!=Object||!r(47)(o)),"Array",{join:function(t){return o.call(i(this),void 0===t?",":t)}})},function(t,n,r){"use strict";var e=r(1),i=r(30),o=r(67),u=r(16),c=[].lastIndexOf,f=!!c&&1/[1].lastIndexOf(1,-0)<0;e(e.P+e.F*(f||!r(47)(c)),"Array",{lastIndexOf:function(t){if(f)return c.apply(this,arguments)||0;var n=i(this),r=u(n.length),e=r-1;for(arguments.length>1&&(e=Math.min(e,o(arguments[1]))),e<0&&(e=r+e);e>=0;e--)if(e in n&&n[e]===t)return e||0;return-1}})},function(t,n,r){"use strict";var e=r(1),i=r(48)(1);e(e.P+e.F*!r(47)([].map,!0),"Array",{map:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(131);e(e.S+e.F*r(4)(function(){function t(){}return!(Array.of.call(t)instanceof t)}),"Array",{of:function(){for(var t=0,n=arguments.length,r=new("function"==typeof this?this:Array)(n);n>t;)i(r,t,arguments[t++]);return r.length=n,r}})},function(t,n,r){"use strict";var e=r(1),i=r(162);e(e.P+e.F*!r(47)([].reduceRight,!0),"Array",{reduceRight:function(t){return i(this,t,arguments.length,arguments[1],!0)}})},function(t,n,r){"use strict";var e=r(1),i=r(162);e(e.P+e.F*!r(47)([].reduce,!0),"Array",{reduce:function(t){return i(this,t,arguments.length,arguments[1],!1)}})},function(t,n,r){"use strict";var e=r(1),i=r(135),o=r(45),u=r(75),c=r(16),f=[].slice;e(e.P+e.F*r(4)(function(){i&&f.call(i)}),"Array",{slice:function(t,n){var r=c(this.length),e=o(this);if(n=void 0===n?r:n,"Array"==e)return f.call(this,t,n);for(var i=u(t,r),a=u(n,r),s=c(a-i),l=Array(s),h=0;h<s;h++)l[h]="String"==e?this.charAt(i+h):this[i+h];return l}})},function(t,n,r){"use strict";var e=r(1),i=r(48)(3);e(e.P+e.F*!r(47)([].some,!0),"Array",{some:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(26),o=r(17),u=r(4),c=[].sort,f=[1,2,3];e(e.P+e.F*(u(function(){f.sort(void 0)})||!u(function(){f.sort(null)})||!r(47)(c)),"Array",{sort:function(t){return void 0===t?c.call(o(this)):c.call(o(this),i(t))}})},function(t,n,r){r(74)("Array")},function(t,n,r){var e=r(1);e(e.S,"Date",{now:function(){return(new Date).getTime()}})},function(t,n,r){"use strict";var e=r(1),i=r(4),o=Date.prototype.getTime,u=function(t){return t>9?t:"0"+t};e(e.P+e.F*(i(function(){return"0385-07-25T07:06:39.999Z"!=new Date(-5e13-1).toISOString()})||!i(function(){new Date(NaN).toISOString()})),"Date",{toISOString:function(){
if(!isFinite(o.call(this)))throw RangeError("Invalid time value");var t=this,n=t.getUTCFullYear(),r=t.getUTCMilliseconds(),e=n<0?"-":n>9999?"+":"";return e+("00000"+Math.abs(n)).slice(e?-6:-4)+"-"+u(t.getUTCMonth()+1)+"-"+u(t.getUTCDate())+"T"+u(t.getUTCHours())+":"+u(t.getUTCMinutes())+":"+u(t.getUTCSeconds())+"."+(r>99?r:"0"+u(r))+"Z"}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(50);e(e.P+e.F*r(4)(function(){return null!==new Date(NaN).toJSON()||1!==Date.prototype.toJSON.call({toISOString:function(){return 1}})}),"Date",{toJSON:function(t){var n=i(this),r=o(n);return"number"!=typeof r||isFinite(r)?n.toISOString():null}})},function(t,n,r){var e=r(7)("toPrimitive"),i=Date.prototype;e in i||r(27)(i,e,r(204))},function(t,n,r){var e=Date.prototype,i="Invalid Date",o="toString",u=e[o],c=e.getTime;new Date(NaN)+""!=i&&r(28)(e,o,function(){var t=c.call(this);return t===t?u.call(this):i})},function(t,n,r){var e=r(1);e(e.P,"Function",{bind:r(163)})},function(t,n,r){"use strict";var e=r(6),i=r(32),o=r(7)("hasInstance"),u=Function.prototype;o in u||r(11).f(u,o,{value:function(t){if("function"!=typeof this||!e(t))return!1;if(!e(this.prototype))return t instanceof this;for(;t=i(t);)if(this.prototype===t)return!0;return!1}})},function(t,n,r){var e=r(11).f,i=r(66),o=r(24),u=Function.prototype,c="name",f=Object.isExtensible||function(){return!0};c in u||r(10)&&e(u,c,{configurable:!0,get:function(){try{var t=this,n=(""+t).match(/^\s*function ([^ (]*)/)[1];return o(t,c)||!f(t)||e(t,c,i(5,n)),n}catch(t){return""}}})},function(t,n,r){var e=r(1),i=r(171),o=Math.sqrt,u=Math.acosh;e(e.S+e.F*!(u&&710==Math.floor(u(Number.MAX_VALUE))&&u(1/0)==1/0),"Math",{acosh:function(t){return(t=+t)<1?NaN:t>94906265.62425156?Math.log(t)+Math.LN2:i(t-1+o(t-1)*o(t+1))}})},function(t,n,r){function e(t){return isFinite(t=+t)&&0!=t?t<0?-e(-t):Math.log(t+Math.sqrt(t*t+1)):t}var i=r(1),o=Math.asinh;i(i.S+i.F*!(o&&1/o(0)>0),"Math",{asinh:e})},function(t,n,r){var e=r(1),i=Math.atanh;e(e.S+e.F*!(i&&1/i(-0)<0),"Math",{atanh:function(t){return 0==(t=+t)?t:Math.log((1+t)/(1-t))/2}})},function(t,n,r){var e=r(1),i=r(142);e(e.S,"Math",{cbrt:function(t){return i(t=+t)*Math.pow(Math.abs(t),1/3)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{clz32:function(t){return(t>>>=0)?31-Math.floor(Math.log(t+.5)*Math.LOG2E):32}})},function(t,n,r){var e=r(1),i=Math.exp;e(e.S,"Math",{cosh:function(t){return(i(t=+t)+i(-t))/2}})},function(t,n,r){var e=r(1),i=r(141);e(e.S+e.F*(i!=Math.expm1),"Math",{expm1:i})},function(t,n,r){var e=r(1),i=r(142),o=Math.pow,u=o(2,-52),c=o(2,-23),f=o(2,127)*(2-c),a=o(2,-126),s=function(t){return t+1/u-1/u};e(e.S,"Math",{fround:function(t){var n,r,e=Math.abs(t),o=i(t);return e<a?o*s(e/a/c)*a*c:(n=(1+c/u)*e,r=n-(n-e),r>f||r!=r?o*(1/0):o*r)}})},function(t,n,r){var e=r(1),i=Math.abs;e(e.S,"Math",{hypot:function(t,n){for(var r,e,o=0,u=0,c=arguments.length,f=0;u<c;)r=i(arguments[u++]),f<r?(e=f/r,o=o*e*e+1,f=r):r>0?(e=r/f,o+=e*e):o+=r;return f===1/0?1/0:f*Math.sqrt(o)}})},function(t,n,r){var e=r(1),i=Math.imul;e(e.S+e.F*r(4)(function(){return-5!=i(4294967295,5)||2!=i.length}),"Math",{imul:function(t,n){var r=65535,e=+t,i=+n,o=r&e,u=r&i;return 0|o*u+((r&e>>>16)*u+o*(r&i>>>16)<<16>>>0)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{log10:function(t){return Math.log(t)/Math.LN10}})},function(t,n,r){var e=r(1);e(e.S,"Math",{log1p:r(171)})},function(t,n,r){var e=r(1);e(e.S,"Math",{log2:function(t){return Math.log(t)/Math.LN2}})},function(t,n,r){var e=r(1);e(e.S,"Math",{sign:r(142)})},function(t,n,r){var e=r(1),i=r(141),o=Math.exp;e(e.S+e.F*r(4)(function(){return-2e-17!=!Math.sinh(-2e-17)}),"Math",{sinh:function(t){return Math.abs(t=+t)<1?(i(t)-i(-t))/2:(o(t-1)-o(-t-1))*(Math.E/2)}})},function(t,n,r){var e=r(1),i=r(141),o=Math.exp;e(e.S,"Math",{tanh:function(t){var n=i(t=+t),r=i(-t);return n==1/0?1:r==1/0?-1:(n-r)/(o(t)+o(-t))}})},function(t,n,r){var e=r(1);e(e.S,"Math",{trunc:function(t){return(t>0?Math.floor:Math.ceil)(t)}})},function(t,n,r){"use strict";var e=r(3),i=r(24),o=r(45),u=r(136),c=r(50),f=r(4),a=r(71).f,s=r(31).f,l=r(11).f,h=r(82).trim,v="Number",p=e[v],d=p,y=p.prototype,g=o(r(70)(y))==v,b="trim"in String.prototype,m=function(t){var n=c(t,!1);if("string"==typeof n&&n.length>2){n=b?n.trim():h(n,3);var r,e,i,o=n.charCodeAt(0);if(43===o||45===o){if(88===(r=n.charCodeAt(2))||120===r)return NaN}else if(48===o){switch(n.charCodeAt(1)){case 66:case 98:e=2,i=49;break;case 79:case 111:e=8,i=55;break;default:return+n}for(var u,f=n.slice(2),a=0,s=f.length;a<s;a++)if((u=f.charCodeAt(a))<48||u>i)return NaN;return parseInt(f,e)}}return+n};if(!p(" 0o1")||!p("0b1")||p("+0x1")){p=function(t){var n=arguments.length<1?0:t,r=this;return r instanceof p&&(g?f(function(){y.valueOf.call(r)}):o(r)!=v)?u(new d(m(n)),r,p):m(n)};for(var x,w=r(10)?a(d):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),S=0;w.length>S;S++)i(d,x=w[S])&&!i(p,x)&&l(p,x,s(d,x));p.prototype=y,y.constructor=p,r(28)(e,v,p)}},function(t,n,r){var e=r(1);e(e.S,"Number",{EPSILON:Math.pow(2,-52)})},function(t,n,r){var e=r(1),i=r(3).isFinite;e(e.S,"Number",{isFinite:function(t){return"number"==typeof t&&i(t)}})},function(t,n,r){var e=r(1);e(e.S,"Number",{isInteger:r(168)})},function(t,n,r){var e=r(1);e(e.S,"Number",{isNaN:function(t){return t!=t}})},function(t,n,r){var e=r(1),i=r(168),o=Math.abs;e(e.S,"Number",{isSafeInteger:function(t){return i(t)&&o(t)<=9007199254740991}})},function(t,n,r){var e=r(1);e(e.S,"Number",{MAX_SAFE_INTEGER:9007199254740991})},function(t,n,r){var e=r(1);e(e.S,"Number",{MIN_SAFE_INTEGER:-9007199254740991})},function(t,n,r){var e=r(1),i=r(178);e(e.S+e.F*(Number.parseFloat!=i),"Number",{parseFloat:i})},function(t,n,r){var e=r(1),i=r(179);e(e.S+e.F*(Number.parseInt!=i),"Number",{parseInt:i})},function(t,n,r){"use strict";var e=r(1),i=r(67),o=r(159),u=r(149),c=1..toFixed,f=Math.floor,a=[0,0,0,0,0,0],s="Number.toFixed: incorrect invocation!",l="0",h=function(t,n){for(var r=-1,e=n;++r<6;)e+=t*a[r],a[r]=e%1e7,e=f(e/1e7)},v=function(t){for(var n=6,r=0;--n>=0;)r+=a[n],a[n]=f(r/t),r=r%t*1e7},p=function(){for(var t=6,n="";--t>=0;)if(""!==n||0===t||0!==a[t]){var r=String(a[t]);n=""===n?r:n+u.call(l,7-r.length)+r}return n},d=function(t,n,r){return 0===n?r:n%2==1?d(t,n-1,r*t):d(t*t,n/2,r)},y=function(t){for(var n=0,r=t;r>=4096;)n+=12,r/=4096;for(;r>=2;)n+=1,r/=2;return n};e(e.P+e.F*(!!c&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!r(4)(function(){c.call({})})),"Number",{toFixed:function(t){var n,r,e,c,f=o(this,s),a=i(t),g="",b=l;if(a<0||a>20)throw RangeError(s);if(f!=f)return"NaN";if(f<=-1e21||f>=1e21)return String(f);if(f<0&&(g="-",f=-f),f>1e-21)if(n=y(f*d(2,69,1))-69,r=n<0?f*d(2,-n,1):f/d(2,n,1),r*=4503599627370496,(n=52-n)>0){for(h(0,r),e=a;e>=7;)h(1e7,0),e-=7;for(h(d(10,e,1),0),e=n-1;e>=23;)v(1<<23),e-=23;v(1<<e),h(1,1),v(2),b=p()}else h(0,r),h(1<<-n,0),b=p()+u.call(l,a);return a>0?(c=b.length,b=g+(c<=a?"0."+u.call(l,a-c)+b:b.slice(0,c-a)+"."+b.slice(c-a))):b=g+b,b}})},function(t,n,r){"use strict";var e=r(1),i=r(4),o=r(159),u=1..toPrecision;e(e.P+e.F*(i(function(){return"1"!==u.call(1,void 0)})||!i(function(){u.call({})})),"Number",{toPrecision:function(t){var n=o(this,"Number#toPrecision: incorrect invocation!");return void 0===t?u.call(n):u.call(n,t)}})},function(t,n,r){var e=r(1);e(e.S+e.F,"Object",{assign:r(172)})},function(t,n,r){var e=r(1);e(e.S,"Object",{create:r(70)})},function(t,n,r){var e=r(1);e(e.S+e.F*!r(10),"Object",{defineProperties:r(173)})},function(t,n,r){var e=r(1);e(e.S+e.F*!r(10),"Object",{defineProperty:r(11).f})},function(t,n,r){var e=r(6),i=r(65).onFreeze;r(49)("freeze",function(t){return function(n){return t&&e(n)?t(i(n)):n}})},function(t,n,r){var e=r(30),i=r(31).f;r(49)("getOwnPropertyDescriptor",function(){return function(t,n){return i(e(t),n)}})},function(t,n,r){r(49)("getOwnPropertyNames",function(){return r(174).f})},function(t,n,r){var e=r(17),i=r(32);r(49)("getPrototypeOf",function(){return function(t){return i(e(t))}})},function(t,n,r){var e=r(6);r(49)("isExtensible",function(t){return function(n){return!!e(n)&&(!t||t(n))}})},function(t,n,r){var e=r(6);r(49)("isFrozen",function(t){return function(n){return!e(n)||!!t&&t(n)}})},function(t,n,r){var e=r(6);r(49)("isSealed",function(t){return function(n){return!e(n)||!!t&&t(n)}})},function(t,n,r){var e=r(1);e(e.S,"Object",{is:r(180)})},function(t,n,r){var e=r(17),i=r(72);r(49)("keys",function(){return function(t){return i(e(t))}})},function(t,n,r){var e=r(6),i=r(65).onFreeze;r(49)("preventExtensions",function(t){return function(n){return t&&e(n)?t(i(n)):n}})},function(t,n,r){var e=r(6),i=r(65).onFreeze;r(49)("seal",function(t){return function(n){return t&&e(n)?t(i(n)):n}})},function(t,n,r){var e=r(1);e(e.S,"Object",{setPrototypeOf:r(144).set})},function(t,n,r){"use strict";var e=r(114),i={};i[r(7)("toStringTag")]="z",i+""!="[object z]"&&r(28)(Object.prototype,"toString",function(){return"[object "+e(this)+"]"},!0)},function(t,n,r){var e=r(1),i=r(178);e(e.G+e.F*(parseFloat!=i),{parseFloat:i})},function(t,n,r){var e=r(1),i=r(179);e(e.G+e.F*(parseInt!=i),{parseInt:i})},function(t,n,r){"use strict";var e,i,o,u=r(69),c=r(3),f=r(53),a=r(114),s=r(1),l=r(6),h=r(26),v=r(68),p=r(79),d=r(146),y=r(151).set,g=r(143)(),b="Promise",m=c.TypeError,x=c.process,w=c[b],x=c.process,S="process"==a(x),_=function(){},O=!!function(){try{var t=w.resolve(1),n=(t.constructor={})[r(7)("species")]=function(t){t(_,_)};return(S||"function"==typeof PromiseRejectionEvent)&&t.then(_)instanceof n}catch(t){}}(),E=function(t,n){return t===n||t===w&&n===o},P=function(t){var n;return!(!l(t)||"function"!=typeof(n=t.then))&&n},j=function(t){return E(w,t)?new F(t):new i(t)},F=i=function(t){var n,r;this.promise=new t(function(t,e){if(void 0!==n||void 0!==r)throw m("Bad Promise constructor");n=t,r=e}),this.resolve=h(n),this.reject=h(r)},M=function(t){try{t()}catch(t){return{error:t}}},A=function(t,n){if(!t._n){t._n=!0;var r=t._c;g(function(){for(var e=t._v,i=1==t._s,o=0;r.length>o;)!function(n){var r,o,u=i?n.ok:n.fail,c=n.resolve,f=n.reject,a=n.domain;try{u?(i||(2==t._h&&I(t),t._h=1),!0===u?r=e:(a&&a.enter(),r=u(e),a&&a.exit()),r===n.promise?f(m("Promise-chain cycle")):(o=P(r))?o.call(r,c,f):c(r)):f(e)}catch(t){f(t)}}(r[o++]);t._c=[],t._n=!1,n&&!t._h&&N(t)})}},N=function(t){y.call(c,function(){var n,r,e,i=t._v;if(T(t)&&(n=M(function(){S?x.emit("unhandledRejection",i,t):(r=c.onunhandledrejection)?r({promise:t,reason:i}):(e=c.console)&&e.error&&e.error("Unhandled promise rejection",i)}),t._h=S||T(t)?2:1),t._a=void 0,n)throw n.error})},T=function(t){if(1==t._h)return!1;for(var n,r=t._a||t._c,e=0;r.length>e;)if(n=r[e++],n.fail||!T(n.promise))return!1;return!0},I=function(t){y.call(c,function(){var n;S?x.emit("rejectionHandled",t):(n=c.onrejectionhandled)&&n({promise:t,reason:t._v})})},k=function(t){var n=this;n._d||(n._d=!0,n=n._w||n,n._v=t,n._s=2,n._a||(n._a=n._c.slice()),A(n,!0))},L=function(t){var n,r=this;if(!r._d){r._d=!0,r=r._w||r;try{if(r===t)throw m("Promise can't be resolved itself");(n=P(t))?g(function(){var e={_w:r,_d:!1};try{n.call(t,f(L,e,1),f(k,e,1))}catch(t){k.call(e,t)}}):(r._v=t,r._s=1,A(r,!1))}catch(t){k.call({_w:r,_d:!1},t)}}};O||(w=function(t){v(this,w,b,"_h"),h(t),e.call(this);try{t(f(L,this,1),f(k,this,1))}catch(t){k.call(this,t)}},e=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1},e.prototype=r(73)(w.prototype,{then:function(t,n){var r=j(d(this,w));return r.ok="function"!=typeof t||t,r.fail="function"==typeof n&&n,r.domain=S?x.domain:void 0,this._c.push(r),this._a&&this._a.push(r),this._s&&A(this,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),F=function(){var t=new e;this.promise=t,this.resolve=f(L,t,1),this.reject=f(k,t,1)}),s(s.G+s.W+s.F*!O,{Promise:w}),r(81)(w,b),r(74)(b),o=r(52)[b],s(s.S+s.F*!O,b,{reject:function(t){var n=j(this);return(0,n.reject)(t),n.promise}}),s(s.S+s.F*(u||!O),b,{resolve:function(t){if(t instanceof w&&E(t.constructor,this))return t;var n=j(this);return(0,n.resolve)(t),n.promise}}),s(s.S+s.F*!(O&&r(123)(function(t){w.all(t).catch(_)})),b,{all:function(t){var n=this,r=j(n),e=r.resolve,i=r.reject,o=M(function(){var r=[],o=0,u=1;p(t,!1,function(t){var c=o++,f=!1;r.push(void 0),u++,n.resolve(t).then(function(t){f||(f=!0,r[c]=t,--u||e(r))},i)}),--u||e(r)});return o&&i(o.error),r.promise},race:function(t){var n=this,r=j(n),e=r.reject,i=M(function(){p(t,!1,function(t){n.resolve(t).then(r.resolve,e)})});return i&&e(i.error),r.promise}})},function(t,n,r){var e=r(1),i=r(26),o=r(2),u=(r(3).Reflect||{}).apply,c=Function.apply;e(e.S+e.F*!r(4)(function(){u(function(){})}),"Reflect",{apply:function(t,n,r){var e=i(t),f=o(r);return u?u(e,n,f):c.call(e,n,f)}})},function(t,n,r){var e=r(1),i=r(70),o=r(26),u=r(2),c=r(6),f=r(4),a=r(163),s=(r(3).Reflect||{}).construct,l=f(function(){function t(){}return!(s(function(){},[],t)instanceof t)}),h=!f(function(){s(function(){})});e(e.S+e.F*(l||h),"Reflect",{construct:function(t,n){o(t),u(n);var r=arguments.length<3?t:o(arguments[2]);if(h&&!l)return s(t,n,r);if(t==r){switch(n.length){case 0:return new t;case 1:return new t(n[0]);case 2:return new t(n[0],n[1]);case 3:return new t(n[0],n[1],n[2]);case 4:return new t(n[0],n[1],n[2],n[3])}var e=[null];return e.push.apply(e,n),new(a.apply(t,e))}var f=r.prototype,v=i(c(f)?f:Object.prototype),p=Function.apply.call(t,v,n);return c(p)?p:v}})},function(t,n,r){var e=r(11),i=r(1),o=r(2),u=r(50);i(i.S+i.F*r(4)(function(){Reflect.defineProperty(e.f({},1,{value:1}),1,{value:2})}),"Reflect",{defineProperty:function(t,n,r){o(t),n=u(n,!0),o(r);try{return e.f(t,n,r),!0}catch(t){return!1}}})},function(t,n,r){var e=r(1),i=r(31).f,o=r(2);e(e.S,"Reflect",{deleteProperty:function(t,n){var r=i(o(t),n);return!(r&&!r.configurable)&&delete t[n]}})},function(t,n,r){"use strict";var e=r(1),i=r(2),o=function(t){this._t=i(t),this._i=0;var n,r=this._k=[];for(n in t)r.push(n)};r(139)(o,"Object",function(){var t,n=this,r=n._k;do{if(n._i>=r.length)return{value:void 0,done:!0}}while(!((t=r[n._i++])in n._t));return{value:t,done:!1}}),e(e.S,"Reflect",{enumerate:function(t){return new o(t)}})},function(t,n,r){var e=r(31),i=r(1),o=r(2);i(i.S,"Reflect",{getOwnPropertyDescriptor:function(t,n){return e.f(o(t),n)}})},function(t,n,r){var e=r(1),i=r(32),o=r(2);e(e.S,"Reflect",{getPrototypeOf:function(t){return i(o(t))}})},function(t,n,r){function e(t,n){var r,c,s=arguments.length<3?t:arguments[2];return a(t)===s?t[n]:(r=i.f(t,n))?u(r,"value")?r.value:void 0!==r.get?r.get.call(s):void 0:f(c=o(t))?e(c,n,s):void 0}var i=r(31),o=r(32),u=r(24),c=r(1),f=r(6),a=r(2);c(c.S,"Reflect",{get:e})},function(t,n,r){var e=r(1);e(e.S,"Reflect",{has:function(t,n){return n in t}})},function(t,n,r){var e=r(1),i=r(2),o=Object.isExtensible;e(e.S,"Reflect",{isExtensible:function(t){return i(t),!o||o(t)}})},function(t,n,r){var e=r(1);e(e.S,"Reflect",{ownKeys:r(177)})},function(t,n,r){var e=r(1),i=r(2),o=Object.preventExtensions;e(e.S,"Reflect",{preventExtensions:function(t){i(t);try{return o&&o(t),!0}catch(t){return!1}}})},function(t,n,r){var e=r(1),i=r(144);i&&e(e.S,"Reflect",{setPrototypeOf:function(t,n){i.check(t,n);try{return i.set(t,n),!0}catch(t){return!1}}})},function(t,n,r){function e(t,n,r){var f,h,v=arguments.length<4?t:arguments[3],p=o.f(s(t),n);if(!p){if(l(h=u(t)))return e(h,n,r,v);p=a(0)}return c(p,"value")?!(!1===p.writable||!l(v)||(f=o.f(v,n)||a(0),f.value=r,i.f(v,n,f),0)):void 0!==p.set&&(p.set.call(v,r),!0)}var i=r(11),o=r(31),u=r(32),c=r(24),f=r(1),a=r(66),s=r(2),l=r(6);f(f.S,"Reflect",{set:e})},function(t,n,r){var e=r(3),i=r(136),o=r(11).f,u=r(71).f,c=r(122),f=r(120),a=e.RegExp,s=a,l=a.prototype,h=/a/g,v=/a/g,p=new a(h)!==h;if(r(10)&&(!p||r(4)(function(){return v[r(7)("match")]=!1,a(h)!=h||a(v)==v||"/a/i"!=a(h,"i")}))){a=function(t,n){var r=this instanceof a,e=c(t),o=void 0===n;return!r&&e&&t.constructor===a&&o?t:i(p?new s(e&&!o?t.source:t,n):s((e=t instanceof a)?t.source:t,e&&o?f.call(t):n),r?this:l,a)};for(var d=u(s),y=0;d.length>y;)!function(t){t in a||o(a,t,{configurable:!0,get:function(){return s[t]},set:function(n){s[t]=n}})}(d[y++]);l.constructor=a,a.prototype=l,r(28)(e,"RegExp",a)}r(74)("RegExp")},function(t,n,r){r(119)("match",1,function(t,n,r){return[function(r){"use strict";var e=t(this),i=void 0==r?void 0:r[n];return void 0!==i?i.call(r,e):new RegExp(r)[n](String(e))},r]})},function(t,n,r){r(119)("replace",2,function(t,n,r){return[function(e,i){"use strict";var o=t(this),u=void 0==e?void 0:e[n];return void 0!==u?u.call(e,o,i):r.call(String(o),e,i)},r]})},function(t,n,r){r(119)("search",1,function(t,n,r){return[function(r){"use strict";var e=t(this),i=void 0==r?void 0:r[n];return void 0!==i?i.call(r,e):new RegExp(r)[n](String(e))},r]})},function(t,n,r){r(119)("split",2,function(t,n,e){"use strict";var i=r(122),o=e,u=[].push,c="split",f="length",a="lastIndex";if("c"=="abbc"[c](/(b)*/)[1]||4!="test"[c](/(?:)/,-1)[f]||2!="ab"[c](/(?:ab)*/)[f]||4!="."[c](/(.?)(.?)/)[f]||"."[c](/()()/)[f]>1||""[c](/.?/)[f]){var s=void 0===/()??/.exec("")[1];e=function(t,n){var r=String(this);if(void 0===t&&0===n)return[];if(!i(t))return o.call(r,t,n);var e,c,l,h,v,p=[],d=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),y=0,g=void 0===n?4294967295:n>>>0,b=new RegExp(t.source,d+"g");for(s||(e=new RegExp("^"+b.source+"$(?!\\s)",d));(c=b.exec(r))&&!((l=c.index+c[0][f])>y&&(p.push(r.slice(y,c.index)),!s&&c[f]>1&&c[0].replace(e,function(){for(v=1;v<arguments[f]-2;v++)void 0===arguments[v]&&(c[v]=void 0)}),c[f]>1&&c.index<r[f]&&u.apply(p,c.slice(1)),h=c[0][f],y=l,p[f]>=g));)b[a]===c.index&&b[a]++;return y===r[f]?!h&&b.test("")||p.push(""):p.push(r.slice(y)),p[f]>g?p.slice(0,g):p}}else"0"[c](void 0,0)[f]&&(e=function(t,n){return void 0===t&&0===n?[]:o.call(this,t,n)});return[function(r,i){var o=t(this),u=void 0==r?void 0:r[n];return void 0!==u?u.call(r,o,i):e.call(String(o),r,i)},e]})},function(t,n,r){"use strict";r(184);var e=r(2),i=r(120),o=r(10),u="toString",c=/./[u],f=function(t){r(28)(RegExp.prototype,u,t,!0)};r(4)(function(){return"/a/b"!=c.call({source:"a",flags:"b"})})?f(function(){var t=e(this);return"/".concat(t.source,"/","flags"in t?t.flags:!o&&t instanceof RegExp?i.call(t):void 0)}):c.name!=u&&f(function(){return c.call(this)})},function(t,n,r){"use strict";r(29)("anchor",function(t){return function(n){return t(this,"a","name",n)}})},function(t,n,r){"use strict";r(29)("big",function(t){return function(){return t(this,"big","","")}})},function(t,n,r){"use strict";r(29)("blink",function(t){return function(){return t(this,"blink","","")}})},function(t,n,r){"use strict";r(29)("bold",function(t){return function(){return t(this,"b","","")}})},function(t,n,r){"use strict";var e=r(1),i=r(147)(!1);e(e.P,"String",{codePointAt:function(t){return i(this,t)}})},function(t,n,r){"use strict";var e=r(1),i=r(16),o=r(148),u="endsWith",c=""[u];e(e.P+e.F*r(134)(u),"String",{endsWith:function(t){var n=o(this,t,u),r=arguments.length>1?arguments[1]:void 0,e=i(n.length),f=void 0===r?e:Math.min(i(r),e),a=String(t);return c?c.call(n,a,f):n.slice(f-a.length,f)===a}})},function(t,n,r){"use strict";r(29)("fixed",function(t){return function(){return t(this,"tt","","")}})},function(t,n,r){"use strict";r(29)("fontcolor",function(t){return function(n){return t(this,"font","color",n)}})},function(t,n,r){"use strict";r(29)("fontsize",function(t){return function(n){return t(this,"font","size",n)}})},function(t,n,r){var e=r(1),i=r(75),o=String.fromCharCode,u=String.fromCodePoint;e(e.S+e.F*(!!u&&1!=u.length),"String",{fromCodePoint:function(t){for(var n,r=[],e=arguments.length,u=0;e>u;){if(n=+arguments[u++],i(n,1114111)!==n)throw RangeError(n+" is not a valid code point");r.push(n<65536?o(n):o(55296+((n-=65536)>>10),n%1024+56320))}return r.join("")}})},function(t,n,r){"use strict";var e=r(1),i=r(148),o="includes";e(e.P+e.F*r(134)(o),"String",{includes:function(t){return!!~i(this,t,o).indexOf(t,arguments.length>1?arguments[1]:void 0)}})},function(t,n,r){"use strict";r(29)("italics",function(t){return function(){return t(this,"i","","")}})},function(t,n,r){"use strict";var e=r(147)(!0);r(140)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},function(t,n,r){"use strict";r(29)("link",function(t){return function(n){return t(this,"a","href",n)}})},function(t,n,r){var e=r(1),i=r(30),o=r(16);e(e.S,"String",{raw:function(t){for(var n=i(t.raw),r=o(n.length),e=arguments.length,u=[],c=0;r>c;)u.push(String(n[c++])),c<e&&u.push(String(arguments[c]));return u.join("")}})},function(t,n,r){var e=r(1);e(e.P,"String",{repeat:r(149)})},function(t,n,r){"use strict";r(29)("small",function(t){return function(){return t(this,"small","","")}})},function(t,n,r){"use strict";var e=r(1),i=r(16),o=r(148),u="startsWith",c=""[u];e(e.P+e.F*r(134)(u),"String",{startsWith:function(t){var n=o(this,t,u),r=i(Math.min(arguments.length>1?arguments[1]:void 0,n.length)),e=String(t);return c?c.call(n,e,r):n.slice(r,r+e.length)===e}})},function(t,n,r){"use strict";r(29)("strike",function(t){return function(){return t(this,"strike","","")}})},function(t,n,r){"use strict";r(29)("sub",function(t){return function(){return t(this,"sub","","")}})},function(t,n,r){"use strict";r(29)("sup",function(t){return function(){return t(this,"sup","","")}})},function(t,n,r){"use strict";r(82)("trim",function(t){return function(){return t(this,3)}})},function(t,n,r){"use strict";var e=r(3),i=r(24),o=r(10),u=r(1),c=r(28),f=r(65).KEY,a=r(4),s=r(126),l=r(81),h=r(76),v=r(7),p=r(182),d=r(153),y=r(206),g=r(205),b=r(138),m=r(2),x=r(30),w=r(50),S=r(66),_=r(70),O=r(174),E=r(31),P=r(11),j=r(72),F=E.f,M=P.f,A=O.f,N=e.Symbol,T=e.JSON,I=T&&T.stringify,k="prototype",L=v("_hidden"),R=v("toPrimitive"),C={}.propertyIsEnumerable,D=s("symbol-registry"),U=s("symbols"),W=s("op-symbols"),G=Object[k],B="function"==typeof N,V=e.QObject,z=!V||!V[k]||!V[k].findChild,q=o&&a(function(){return 7!=_(M({},"a",{get:function(){return M(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=F(G,n);e&&delete G[n],M(t,n,r),e&&t!==G&&M(G,n,e)}:M,K=function(t){var n=U[t]=_(N[k]);return n._k=t,n},J=B&&"symbol"==typeof N.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof N},Y=function(t,n,r){return t===G&&Y(W,n,r),m(t),n=w(n,!0),m(r),i(U,n)?(r.enumerable?(i(t,L)&&t[L][n]&&(t[L][n]=!1),r=_(r,{enumerable:S(0,!1)})):(i(t,L)||M(t,L,S(1,{})),t[L][n]=!0),q(t,n,r)):M(t,n,r)},H=function(t,n){m(t);for(var r,e=g(n=x(n)),i=0,o=e.length;o>i;)Y(t,r=e[i++],n[r]);return t},$=function(t,n){return void 0===n?_(t):H(_(t),n)},X=function(t){var n=C.call(this,t=w(t,!0));return!(this===G&&i(U,t)&&!i(W,t))&&(!(n||!i(this,t)||!i(U,t)||i(this,L)&&this[L][t])||n)},Q=function(t,n){if(t=x(t),n=w(n,!0),t!==G||!i(U,n)||i(W,n)){var r=F(t,n);return!r||!i(U,n)||i(t,L)&&t[L][n]||(r.enumerable=!0),r}},Z=function(t){for(var n,r=A(x(t)),e=[],o=0;r.length>o;)i(U,n=r[o++])||n==L||n==f||e.push(n);return e},tt=function(t){for(var n,r=t===G,e=A(r?W:x(t)),o=[],u=0;e.length>u;)!i(U,n=e[u++])||r&&!i(G,n)||o.push(U[n]);return o};B||(N=function(){if(this instanceof N)throw TypeError("Symbol is not a constructor!");var t=h(arguments.length>0?arguments[0]:void 0),n=function(r){this===G&&n.call(W,r),i(this,L)&&i(this[L],t)&&(this[L][t]=!1),q(this,t,S(1,r))};return o&&z&&q(G,t,{configurable:!0,set:n}),K(t)},c(N[k],"toString",function(){return this._k}),E.f=Q,P.f=Y,r(71).f=O.f=Z,r(116).f=X,r(125).f=tt,o&&!r(69)&&c(G,"propertyIsEnumerable",X,!0),p.f=function(t){return K(v(t))}),u(u.G+u.W+u.F*!B,{Symbol:N});for(var nt="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),rt=0;nt.length>rt;)v(nt[rt++]);for(var nt=j(v.store),rt=0;nt.length>rt;)d(nt[rt++]);u(u.S+u.F*!B,"Symbol",{for:function(t){return i(D,t+="")?D[t]:D[t]=N(t)},keyFor:function(t){if(J(t))return y(D,t);throw TypeError(t+" is not a symbol!")},useSetter:function(){z=!0},useSimple:function(){z=!1}}),u(u.S+u.F*!B,"Object",{create:$,defineProperty:Y,defineProperties:H,getOwnPropertyDescriptor:Q,getOwnPropertyNames:Z,getOwnPropertySymbols:tt}),T&&u(u.S+u.F*(!B||a(function(){var t=N();return"[null]"!=I([t])||"{}"!=I({a:t})||"{}"!=I(Object(t))})),"JSON",{stringify:function(t){if(void 0!==t&&!J(t)){for(var n,r,e=[t],i=1;arguments.length>i;)e.push(arguments[i++]);return n=e[1],"function"==typeof n&&(r=n),!r&&b(n)||(n=function(t,n){if(r&&(n=r.call(this,t,n)),!J(n))return n}),e[1]=n,I.apply(T,e)}}}),N[k][R]||r(27)(N[k],R,N[k].valueOf),l(N,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(t,n,r){"use strict";var e=r(1),i=r(127),o=r(152),u=r(2),c=r(75),f=r(16),a=r(6),s=r(3).ArrayBuffer,l=r(146),h=o.ArrayBuffer,v=o.DataView,p=i.ABV&&s.isView,d=h.prototype.slice,y=i.VIEW,g="ArrayBuffer";e(e.G+e.W+e.F*(s!==h),{ArrayBuffer:h}),e(e.S+e.F*!i.CONSTR,g,{isView:function(t){return p&&p(t)||a(t)&&y in t}}),e(e.P+e.U+e.F*r(4)(function(){return!new h(2).slice(1,void 0).byteLength}),g,{slice:function(t,n){if(void 0!==d&&void 0===n)return d.call(u(this),t);for(var r=u(this).byteLength,e=c(t,r),i=c(void 0===n?r:n,r),o=new(l(this,h))(f(i-e)),a=new v(this),s=new v(o),p=0;e<i;)s.setUint8(p++,a.getUint8(e++));return o}}),r(74)(g)},function(t,n,r){var e=r(1);e(e.G+e.W+e.F*!r(127).ABV,{DataView:r(152).DataView})},function(t,n,r){r(55)("Float32",4,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Float64",8,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Int16",2,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Int32",4,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Int8",1,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint16",2,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint32",4,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint8",1,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint8",1,function(t){return function(n,r,e){return t(this,n,r,e)}},!0)},function(t,n,r){"use strict";var e=r(166);r(118)("WeakSet",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{add:function(t){return e.def(this,t,!0)}},e,!1,!0)},function(t,n,r){"use strict";var e=r(1),i=r(117)(!0);e(e.P,"Array",{includes:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r(78)("includes")},function(t,n,r){var e=r(1),i=r(143)(),o=r(3).process,u="process"==r(45)(o);e(e.G,{asap:function(t){var n=u&&o.domain;i(n?n.bind(t):t)}})},function(t,n,r){var e=r(1),i=r(45);e(e.S,"Error",{isError:function(t){return"Error"===i(t)}})},function(t,n,r){var e=r(1);e(e.P+e.R,"Map",{toJSON:r(165)("Map")})},function(t,n,r){var e=r(1);e(e.S,"Math",{iaddh:function(t,n,r,e){var i=t>>>0,o=n>>>0,u=r>>>0;return o+(e>>>0)+((i&u|(i|u)&~(i+u>>>0))>>>31)|0}})},function(t,n,r){var e=r(1);e(e.S,"Math",{imulh:function(t,n){var r=65535,e=+t,i=+n,o=e&r,u=i&r,c=e>>16,f=i>>16,a=(c*u>>>0)+(o*u>>>16);return c*f+(a>>16)+((o*f>>>0)+(a&r)>>16)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{isubh:function(t,n,r,e){var i=t>>>0,o=n>>>0,u=r>>>0;return o-(e>>>0)-((~i&u|~(i^u)&i-u>>>0)>>>31)|0}})},function(t,n,r){var e=r(1);e(e.S,"Math",{umulh:function(t,n){var r=65535,e=+t,i=+n,o=e&r,u=i&r,c=e>>>16,f=i>>>16,a=(c*u>>>0)+(o*u>>>16);return c*f+(a>>>16)+((o*f>>>0)+(a&r)>>>16)}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(26),u=r(11);r(10)&&e(e.P+r(124),"Object",{__defineGetter__:function(t,n){u.f(i(this),t,{get:o(n),enumerable:!0,configurable:!0})}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(26),u=r(11);r(10)&&e(e.P+r(124),"Object",{__defineSetter__:function(t,n){u.f(i(this),t,{set:o(n),enumerable:!0,configurable:!0})}})},function(t,n,r){var e=r(1),i=r(176)(!0);e(e.S,"Object",{entries:function(t){return i(t)}})},function(t,n,r){var e=r(1),i=r(177),o=r(30),u=r(31),c=r(131);e(e.S,"Object",{getOwnPropertyDescriptors:function(t){for(var n,r=o(t),e=u.f,f=i(r),a={},s=0;f.length>s;)c(a,n=f[s++],e(r,n));return a}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(50),u=r(32),c=r(31).f;r(10)&&e(e.P+r(124),"Object",{__lookupGetter__:function(t){var n,r=i(this),e=o(t,!0);do{if(n=c(r,e))return n.get}while(r=u(r))}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(50),u=r(32),c=r(31).f;r(10)&&e(e.P+r(124),"Object",{__lookupSetter__:function(t){var n,r=i(this),e=o(t,!0);do{if(n=c(r,e))return n.set}while(r=u(r))}})},function(t,n,r){var e=r(1),i=r(176)(!1);e(e.S,"Object",{values:function(t){return i(t)}})},function(t,n,r){"use strict";var e=r(1),i=r(3),o=r(52),u=r(143)(),c=r(7)("observable"),f=r(26),a=r(2),s=r(68),l=r(73),h=r(27),v=r(79),p=v.RETURN,d=function(t){return null==t?void 0:f(t)},y=function(t){var n=t._c;n&&(t._c=void 0,n())},g=function(t){return void 0===t._o},b=function(t){g(t)||(t._o=void 0,y(t))},m=function(t,n){a(t),this._c=void 0,this._o=t,t=new x(this);try{var r=n(t),e=r;null!=r&&("function"==typeof r.unsubscribe?r=function(){e.unsubscribe()}:f(r),this._c=r)}catch(n){return void t.error(n)}g(this)&&y(this)};m.prototype=l({},{unsubscribe:function(){b(this)}});var x=function(t){this._s=t};x.prototype=l({},{next:function(t){var n=this._s;if(!g(n)){var r=n._o;try{var e=d(r.next);if(e)return e.call(r,t)}catch(t){try{b(n)}finally{throw t}}}},error:function(t){var n=this._s;if(g(n))throw t;var r=n._o;n._o=void 0;try{var e=d(r.error);if(!e)throw t;t=e.call(r,t)}catch(t){try{y(n)}finally{throw t}}return y(n),t},complete:function(t){var n=this._s;if(!g(n)){var r=n._o;n._o=void 0;try{var e=d(r.complete);t=e?e.call(r,t):void 0}catch(t){try{y(n)}finally{throw t}}return y(n),t}}});var w=function(t){s(this,w,"Observable","_f")._f=f(t)};l(w.prototype,{subscribe:function(t){return new m(t,this._f)},forEach:function(t){var n=this;return new(o.Promise||i.Promise)(function(r,e){f(t);var i=n.subscribe({next:function(n){try{return t(n)}catch(t){e(t),i.unsubscribe()}},error:e,complete:r})})}}),l(w,{from:function(t){var n="function"==typeof this?this:w,r=d(a(t)[c]);if(r){var e=a(r.call(t));return e.constructor===n?e:new n(function(t){return e.subscribe(t)})}return new n(function(n){var r=!1;return u(function(){if(!r){try{if(v(t,!1,function(t){if(n.next(t),r)return p})===p)return}catch(t){if(r)throw t;return void n.error(t)}n.complete()}}),function(){r=!0}})},of:function(){for(var t=0,n=arguments.length,r=Array(n);t<n;)r[t]=arguments[t++];return new("function"==typeof this?this:w)(function(t){var n=!1;return u(function(){if(!n){for(var e=0;e<r.length;++e)if(t.next(r[e]),n)return;t.complete()}}),function(){n=!0}})}}),h(w.prototype,c,function(){return this}),e(e.G,{Observable:w}),r(74)("Observable")},function(t,n,r){var e=r(54),i=r(2),o=e.key,u=e.set;e.exp({defineMetadata:function(t,n,r,e){u(t,n,i(r),o(e))}})},function(t,n,r){var e=r(54),i=r(2),o=e.key,u=e.map,c=e.store;e.exp({deleteMetadata:function(t,n){var r=arguments.length<3?void 0:o(arguments[2]),e=u(i(n),r,!1);if(void 0===e||!e.delete(t))return!1;if(e.size)return!0;var f=c.get(n);return f.delete(r),!!f.size||c.delete(n)}})},function(t,n,r){var e=r(185),i=r(161),o=r(54),u=r(2),c=r(32),f=o.keys,a=o.key,s=function(t,n){var r=f(t,n),o=c(t);if(null===o)return r;var u=s(o,n);return u.length?r.length?i(new e(r.concat(u))):u:r};o.exp({getMetadataKeys:function(t){return s(u(t),arguments.length<2?void 0:a(arguments[1]))}})},function(t,n,r){var e=r(54),i=r(2),o=r(32),u=e.has,c=e.get,f=e.key,a=function(t,n,r){if(u(t,n,r))return c(t,n,r);var e=o(n);return null!==e?a(t,e,r):void 0};e.exp({getMetadata:function(t,n){return a(t,i(n),arguments.length<3?void 0:f(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=e.keys,u=e.key;e.exp({getOwnMetadataKeys:function(t){
return o(i(t),arguments.length<2?void 0:u(arguments[1]))}})},function(t,n,r){var e=r(54),i=r(2),o=e.get,u=e.key;e.exp({getOwnMetadata:function(t,n){return o(t,i(n),arguments.length<3?void 0:u(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=r(32),u=e.has,c=e.key,f=function(t,n,r){if(u(t,n,r))return!0;var e=o(n);return null!==e&&f(t,e,r)};e.exp({hasMetadata:function(t,n){return f(t,i(n),arguments.length<3?void 0:c(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=e.has,u=e.key;e.exp({hasOwnMetadata:function(t,n){return o(t,i(n),arguments.length<3?void 0:u(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=r(26),u=e.key,c=e.set;e.exp({metadata:function(t,n){return function(r,e){c(t,n,(void 0!==e?i:o)(r),u(e))}}})},function(t,n,r){var e=r(1);e(e.P+e.R,"Set",{toJSON:r(165)("Set")})},function(t,n,r){"use strict";var e=r(1),i=r(147)(!0);e(e.P,"String",{at:function(t){return i(this,t)}})},function(t,n,r){"use strict";var e=r(1),i=r(46),o=r(16),u=r(122),c=r(120),f=RegExp.prototype,a=function(t,n){this._r=t,this._s=n};r(139)(a,"RegExp String",function(){var t=this._r.exec(this._s);return{value:t,done:null===t}}),e(e.P,"String",{matchAll:function(t){if(i(this),!u(t))throw TypeError(t+" is not a regexp!");var n=String(this),r="flags"in f?String(t.flags):c.call(t),e=new RegExp(t.source,~r.indexOf("g")?r:"g"+r);return e.lastIndex=o(t.lastIndex),new a(e,n)}})},function(t,n,r){"use strict";var e=r(1),i=r(181);e(e.P,"String",{padEnd:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0,!1)}})},function(t,n,r){"use strict";var e=r(1),i=r(181);e(e.P,"String",{padStart:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0,!0)}})},function(t,n,r){"use strict";r(82)("trimLeft",function(t){return function(){return t(this,1)}},"trimStart")},function(t,n,r){"use strict";r(82)("trimRight",function(t){return function(){return t(this,2)}},"trimEnd")},function(t,n,r){r(153)("asyncIterator")},function(t,n,r){r(153)("observable")},function(t,n,r){var e=r(1);e(e.S,"System",{global:r(3)})},function(t,n,r){for(var e=r(155),i=r(28),o=r(3),u=r(27),c=r(80),f=r(7),a=f("iterator"),s=f("toStringTag"),l=c.Array,h=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],v=0;v<5;v++){var p,d=h[v],y=o[d],g=y&&y.prototype;if(g){g[a]||u(g,a,l),g[s]||u(g,s,d),c[d]=l;for(p in e)g[p]||i(g,p,e[p],!0)}}},function(t,n,r){var e=r(1),i=r(151);e(e.G+e.B,{setImmediate:i.set,clearImmediate:i.clear})},function(t,n,r){var e=r(3),i=r(1),o=r(121),u=r(207),c=e.navigator,f=!!c&&/MSIE .\./.test(c.userAgent),a=function(t){return f?function(n,r){return t(o(u,[].slice.call(arguments,2),"function"==typeof n?n:Function(n)),r)}:t};i(i.G+i.B+i.F*f,{setTimeout:a(e.setTimeout),setInterval:a(e.setInterval)})},function(t,n,r){r(330),r(269),r(271),r(270),r(273),r(275),r(280),r(274),r(272),r(282),r(281),r(277),r(278),r(276),r(268),r(279),r(283),r(284),r(236),r(238),r(237),r(286),r(285),r(256),r(266),r(267),r(257),r(258),r(259),r(260),r(261),r(262),r(263),r(264),r(265),r(239),r(240),r(241),r(242),r(243),r(244),r(245),r(246),r(247),r(248),r(249),r(250),r(251),r(252),r(253),r(254),r(255),r(317),r(322),r(329),r(320),r(312),r(313),r(318),r(323),r(325),r(308),r(309),r(310),r(311),r(314),r(315),r(316),r(319),r(321),r(324),r(326),r(327),r(328),r(231),r(233),r(232),r(235),r(234),r(220),r(218),r(224),r(221),r(227),r(229),r(217),r(223),r(214),r(228),r(212),r(226),r(225),r(219),r(222),r(211),r(213),r(216),r(215),r(230),r(155),r(302),r(307),r(184),r(303),r(304),r(305),r(306),r(287),r(183),r(185),r(186),r(342),r(331),r(332),r(337),r(340),r(341),r(335),r(338),r(336),r(339),r(333),r(334),r(288),r(289),r(290),r(291),r(292),r(295),r(293),r(294),r(296),r(297),r(298),r(299),r(301),r(300),r(343),r(369),r(372),r(371),r(373),r(374),r(370),r(375),r(376),r(354),r(357),r(353),r(351),r(352),r(355),r(356),r(346),r(368),r(377),r(345),r(347),r(349),r(348),r(350),r(359),r(360),r(362),r(361),r(364),r(363),r(365),r(366),r(367),r(344),r(358),r(380),r(379),r(378),t.exports=r(52)},function(t,n){function r(t,n){if("string"==typeof n)return t.insertAdjacentHTML("afterend",n);var r=t.nextSibling;return r?t.parentNode.insertBefore(n,r):t.parentNode.appendChild(n)}t.exports=r},,,,,,,,,function(t,n,r){(function(n,r){!function(n){"use strict";function e(t,n,r,e){var i=n&&n.prototype instanceof o?n:o,u=Object.create(i.prototype),c=new p(e||[]);return u._invoke=s(t,r,c),u}function i(t,n,r){try{return{type:"normal",arg:t.call(n,r)}}catch(t){return{type:"throw",arg:t}}}function o(){}function u(){}function c(){}function f(t){["next","throw","return"].forEach(function(n){t[n]=function(t){return this._invoke(n,t)}})}function a(t){function n(r,e,o,u){var c=i(t[r],t,e);if("throw"!==c.type){var f=c.arg,a=f.value;return a&&"object"==typeof a&&m.call(a,"__await")?Promise.resolve(a.__await).then(function(t){n("next",t,o,u)},function(t){n("throw",t,o,u)}):Promise.resolve(a).then(function(t){f.value=t,o(f)},u)}u(c.arg)}function e(t,r){function e(){return new Promise(function(e,i){n(t,r,e,i)})}return o=o?o.then(e,e):e()}"object"==typeof r&&r.domain&&(n=r.domain.bind(n));var o;this._invoke=e}function s(t,n,r){var e=P;return function(o,u){if(e===F)throw new Error("Generator is already running");if(e===M){if("throw"===o)throw u;return y()}for(r.method=o,r.arg=u;;){var c=r.delegate;if(c){var f=l(c,r);if(f){if(f===A)continue;return f}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if(e===P)throw e=M,r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);e=F;var a=i(t,n,r);if("normal"===a.type){if(e=r.done?M:j,a.arg===A)continue;return{value:a.arg,done:r.done}}"throw"===a.type&&(e=M,r.method="throw",r.arg=a.arg)}}}function l(t,n){var r=t.iterator[n.method];if(r===g){if(n.delegate=null,"throw"===n.method){if(t.iterator.return&&(n.method="return",n.arg=g,l(t,n),"throw"===n.method))return A;n.method="throw",n.arg=new TypeError("The iterator does not provide a 'throw' method")}return A}var e=i(r,t.iterator,n.arg);if("throw"===e.type)return n.method="throw",n.arg=e.arg,n.delegate=null,A;var o=e.arg;return o?o.done?(n[t.resultName]=o.value,n.next=t.nextLoc,"return"!==n.method&&(n.method="next",n.arg=g),n.delegate=null,A):o:(n.method="throw",n.arg=new TypeError("iterator result is not an object"),n.delegate=null,A)}function h(t){var n={tryLoc:t[0]};1 in t&&(n.catchLoc=t[1]),2 in t&&(n.finallyLoc=t[2],n.afterLoc=t[3]),this.tryEntries.push(n)}function v(t){var n=t.completion||{};n.type="normal",delete n.arg,t.completion=n}function p(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(h,this),this.reset(!0)}function d(t){if(t){var n=t[w];if(n)return n.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var r=-1,e=function n(){for(;++r<t.length;)if(m.call(t,r))return n.value=t[r],n.done=!1,n;return n.value=g,n.done=!0,n};return e.next=e}}return{next:y}}function y(){return{value:g,done:!0}}var g,b=Object.prototype,m=b.hasOwnProperty,x="function"==typeof Symbol?Symbol:{},w=x.iterator||"@@iterator",S=x.asyncIterator||"@@asyncIterator",_=x.toStringTag||"@@toStringTag",O="object"==typeof t,E=n.regeneratorRuntime;if(E)return void(O&&(t.exports=E));E=n.regeneratorRuntime=O?t.exports:{},E.wrap=e;var P="suspendedStart",j="suspendedYield",F="executing",M="completed",A={},N={};N[w]=function(){return this};var T=Object.getPrototypeOf,I=T&&T(T(d([])));I&&I!==b&&m.call(I,w)&&(N=I);var k=c.prototype=o.prototype=Object.create(N);u.prototype=k.constructor=c,c.constructor=u,c[_]=u.displayName="GeneratorFunction",E.isGeneratorFunction=function(t){var n="function"==typeof t&&t.constructor;return!!n&&(n===u||"GeneratorFunction"===(n.displayName||n.name))},E.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,c):(t.__proto__=c,_ in t||(t[_]="GeneratorFunction")),t.prototype=Object.create(k),t},E.awrap=function(t){return{__await:t}},f(a.prototype),a.prototype[S]=function(){return this},E.AsyncIterator=a,E.async=function(t,n,r,i){var o=new a(e(t,n,r,i));return E.isGeneratorFunction(n)?o:o.next().then(function(t){return t.done?t.value:o.next()})},f(k),k[_]="Generator",k.toString=function(){return"[object Generator]"},E.keys=function(t){var n=[];for(var r in t)n.push(r);return n.reverse(),function r(){for(;n.length;){var e=n.pop();if(e in t)return r.value=e,r.done=!1,r}return r.done=!0,r}},E.values=d,p.prototype={constructor:p,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=g,this.done=!1,this.delegate=null,this.method="next",this.arg=g,this.tryEntries.forEach(v),!t)for(var n in this)"t"===n.charAt(0)&&m.call(this,n)&&!isNaN(+n.slice(1))&&(this[n]=g)},stop:function(){this.done=!0;var t=this.tryEntries[0],n=t.completion;if("throw"===n.type)throw n.arg;return this.rval},dispatchException:function(t){function n(n,e){return o.type="throw",o.arg=t,r.next=n,e&&(r.method="next",r.arg=g),!!e}if(this.done)throw t;for(var r=this,e=this.tryEntries.length-1;e>=0;--e){var i=this.tryEntries[e],o=i.completion;if("root"===i.tryLoc)return n("end");if(i.tryLoc<=this.prev){var u=m.call(i,"catchLoc"),c=m.call(i,"finallyLoc");if(u&&c){if(this.prev<i.catchLoc)return n(i.catchLoc,!0);if(this.prev<i.finallyLoc)return n(i.finallyLoc)}else if(u){if(this.prev<i.catchLoc)return n(i.catchLoc,!0)}else{if(!c)throw new Error("try statement without catch or finally");if(this.prev<i.finallyLoc)return n(i.finallyLoc)}}}},abrupt:function(t,n){for(var r=this.tryEntries.length-1;r>=0;--r){var e=this.tryEntries[r];if(e.tryLoc<=this.prev&&m.call(e,"finallyLoc")&&this.prev<e.finallyLoc){var i=e;break}}i&&("break"===t||"continue"===t)&&i.tryLoc<=n&&n<=i.finallyLoc&&(i=null);var o=i?i.completion:{};return o.type=t,o.arg=n,i?(this.method="next",this.next=i.finallyLoc,A):this.complete(o)},complete:function(t,n){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&n&&(this.next=n),A},finish:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),v(r),A}},catch:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.tryLoc===t){var e=r.completion;if("throw"===e.type){var i=e.arg;v(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,n,r){return this.delegate={iterator:d(t),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=g),A}}}("object"==typeof n?n:"object"==typeof window?window:"object"==typeof self?self:this)}).call(n,function(){return this}(),r(158))}])</script><script src="/./main.0cf68a.js"></script><script>!function(){!function(e){var t=document.createElement("script");document.getElementsByTagName("body")[0].appendChild(t),t.setAttribute("src",e)}("/slider.e37972.js")}()</script>


    
<div class="tools-col" q-class="show:isShow,hide:isShow|isFalse" q-on="click:stop(e)">
  <div class="tools-nav header-menu">
    
    
      
      
      
    
      
      
      
    
      
      
      
    
    

    <ul style="width: 70%">
    
    
      
      <li style="width: 33.333333333333336%" q-on="click: openSlider(e, 'innerArchive')"><a href="javascript:void(0)" q-class="active:innerArchive">所有文章</a></li>
      
        
      
      <li style="width: 33.333333333333336%" q-on="click: openSlider(e, 'friends')"><a href="javascript:void(0)" q-class="active:friends">友链</a></li>
      
        
      
      <li style="width: 33.333333333333336%" q-on="click: openSlider(e, 'aboutme')"><a href="javascript:void(0)" q-class="active:aboutme">关于我</a></li>
      
        
    </ul>
  </div>
  <div class="tools-wrap">
    
    	<section class="tools-section tools-section-all" q-show="innerArchive">
        <div class="search-wrap">
          <input class="search-ipt" q-model="search" type="text" placeholder="find something…">
          <i class="icon-search icon" q-show="search|isEmptyStr"></i>
          <i class="icon-close icon" q-show="search|isNotEmptyStr" q-on="click:clearChose(e)"></i>
        </div>
        <div class="widget tagcloud search-tag">
          <p class="search-tag-wording">tag:</p>
          <label class="search-switch">
            <input type="checkbox" q-on="click:toggleTag(e)" q-attr="checked:showTags">
          </label>
          <ul class="article-tag-list" q-show="showTags">
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color3">总结</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color3">线程</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color2">Hexo博客</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color5">网络编程</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color3">进程</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color1">Linux</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color1">C++11</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color2">Docker</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color5">设计模式</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color3">剑指</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color3">算法</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color5">蘑菇博客</a>
              </li>
             
              <li class="article-tag-list-item">
                <a href="javascript:void(0)" class="js-tag color5">问答平台</a>
              </li>
            
            <div class="clearfix"></div>
          </ul>
        </div>
        <ul class="search-ul">
          <p q-show="jsonFail" style="padding: 20px; font-size: 12px;">
            缺失模块。<br/>1、请确保node版本大于6.2<br/>2、在博客根目录（注意不是yilia根目录）执行以下命令：<br/> npm i hexo-generator-json-content --save<br/><br/>
            3、在根目录_config.yml里添加配置：
<pre style="font-size: 12px;" q-show="jsonFail">
  jsonContent:
    meta: false
    pages: false
    posts:
      title: true
      date: true
      path: true
      text: false
      raw: false
      content: false
      slug: false
      updated: false
      comments: false
      link: false
      permalink: false
      excerpt: false
      categories: false
      tags: true
</pre>
          </p>
          <li class="search-li" q-repeat="items" q-show="isShow">
            <a q-attr="href:path|urlformat" class="search-title"><i class="icon-quo-left icon"></i><span q-text="title"></span></a>
            <p class="search-time">
              <i class="icon-calendar icon"></i>
              <span q-text="date|dateformat"></span>
            </p>
            <p class="search-tag">
              <i class="icon-price-tags icon"></i>
              <span q-repeat="tags" q-on="click:choseTag(e, name)" q-text="name|tagformat"></span>
            </p>
          </li>
        </ul>
    	</section>
    

    
    	<section class="tools-section tools-section-friends" q-show="friends">
  		
        <ul class="search-ul">
          
            <li class="search-li">
              <a href="https://blog.csdn.net/qq_35131503" target="_blank" class="search-title"><i class="icon-quo-left icon"></i>CSDN博客</a>
            </li>
          
        </ul>
  		
    	</section>
    

    
    	<section class="tools-section tools-section-me" q-show="aboutme">
  	  	
  	  		<div class="aboutme-wrap" id="js-aboutme">zero to one&lt;br&gt;&lt;br&gt;记录过程&lt;br&gt;与君共勉</div>
  	  	
    	</section>
    
  </div>
  
</div>
    <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                      <div class="pswp__preloader__cut">
                        <div class="pswp__preloader__donut"></div>
                      </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div> 
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>
  </div>
</body>
</html>